3
votes

iOS 10, the gift that keeps on breaking, seems to have changed another behavior.

Assume two UIViewControllers are pushed onto a UINavigationController.

On iOS 8/9, calling navigationController?.popViewController(animated: true) to pop the top UIViewController (say VC2) caused viewDidLayoutSubviews on the bottom view controller (say VC1) to get called.

We relied on this to refresh VC1. Sometimes VC2 adds subviews to VC1 (via the data model), and this needs to get reflected when popping back to VC1.

Accurate frame information is required. We can't use viewWillAppear because frame data is wrong on iOS 9. The problem with viewDidAppear is that there is a momentary glitch between seeing the view initially and the adjustment.

Now VC1's viewDidLayoutSubviews does not get invoked when popping VC2.

1) Is this a bug for viewDidLayoutSubviews to not get invoked?

2) What's the right way to refresh view controllers when popping with UINavigationController?

2
I assume viewDidLayoutSubviews is called at some point. Is it called together with the other controller (VC2)?Léo Natan
@LeoNatan yes it is is called when VC1 appears the first timeCrashalot
Actually sounds like an acceptable behavior. If you update data source in viewDidLayoutSubviews, that's not good.Léo Natan
@LeoNatan no we don't update the data source in viewDidLayoutSubviews. the data source is modified in VC2, but VC1 should reflect the new data (which causes new subviews to appear).Crashalot
@Crashalot Then what I put in my answer is correct. You should update the data in viewWillAppear and add/update any new views as well. The new views should be setup based on the view controller's view size at the time. viewDidLayoutSubviews can be used as normal to adjust the subview's frames.rmaddy

2 Answers

3
votes

Relying on viewDidLayoutSubviews was never the proper solution. UIViewController provides viewWillAppear: or viewDidAppear: for such a use. When VC2 is popped from the navigation controller, those two methods will be called on VC1 to let you know that is will be or now is visible again.

viewDidLayoutSubviews should only be used to adjust view frames and layout.

viewWill|DidAppear: should be used to handle the view controller becoming visible originally or again. In your case you should use this to update data and add/update views as needed. Those new views should be setup based on the view controller's current frame. They will be adjusted in your implementation of viewDidLayoutSubviews as needed.

2
votes

I will complement rmaddy's answer. You need to decouple performing layout and updating your data. If your flow is such that data should updated as the view is about to appear, you should update your controller's view-backing data in viewWillAppear:, reload your views and then mark the view as needing layout using setNeedsLayout. This will cause the system to perform a layout on the controller's view, and will trigger the layout. This way, you can ensure the layout is performed once the view is ready, and not before (as often the case is in viewWillAppear:.