What is the difference between -viewWillAppear: and -viewDidAppear:?

What is the difference between -[UIViewController viewWillAppear:] and -[UIViewController viewDidAppear:]?


Solution 1:

In general, this is what I do:

  1. ViewDidLoad - Whenever I'm adding controls to a view that should appear together with the view, right away, I put it in the ViewDidLoad method. Basically, this method is called whenever the view was loaded into memory. So for example, if my view is a form with 3 labels, I would add the labels here; the view will never exist without these forms.

  2. ViewWillAppear: I use ViewWillAppear usually just to update the data on the form. So, for the example above, I would use this to actually load the data from my domain into the form. Creation of UIViews is fairly expensive, and you should avoid as much as possible doing that on the ViewWillAppear method because when this gets called, it means that the iPhone is already ready to show the UIView to the user, and anything heavy you do here will impact performance in a very visible manner (like animations being delayed, etc).

  3. ViewDidAppear: Finally, I'm using ViewDidAppear to start off new threads to things that would take a long time to execute, like for example doing a web service call to get extra data for the form above. The good thing is that because the view already exists and is being displayed to the user, you can show a nice "Waiting" message to the user while you get the data.

Solution 2:

viewDidLoad ===>>> Put your initialization code here. Don't put dynamic data that might change during the view lifecycle. So, if you are pulling data from core data you don't want to do it here if this could change during the life of the view. For example: say you have a tab controller. You switch from tab1 to tab2 and change something on the model in tab2. If you come back to tab1 and your model code was done in viewDidLoad this would not be updated (assuming you're not using KVO or NSFetchedResultsController, etc.).

viewWillAppear ===>>> This is called every time the view is about to appear, whether or not the view is already in memory. Put your dynamic code here, such as model logic.

viewDidAppear ===>>> Put expensive operations here that you only want to do if you're sure the view is onscreen, such as network calls.

Notice: if your app is backgrounded and returns to the foreground you need to handle this using NSNotificationCenter. I wrote the code out for that in the comments below. You might think viewWillAppear/viewDidAppear will fire. Put a break point there and test it. It doesn't fire. So, if something has changed for your app while it was in the background you'll need to update that using notifications.

Solution 3:

The viewWillAppear method is called before loading the actual view.

The viewDidAppear method is called when the view is already loaded, and you want to show something.

Solution 4:

viewWillAppear:
■ Called before the view is added to the windows’ view hierarchy
■ Called before [vc.view layoutSubviews] (if necessary)
viewDidAppear:
■ Called after the view is added to the view hierarchy
■ Called after [vc.view layoutSubviews] (if necessary)

Solution 5:

A few observations:

  • The viewDidLoad method is called when the view is first instantiated. IBOutlet references are hooked up by the time this has been called, but not before. The frame of the view may not be established by the time this has been called, though. This is a great place to add/configure subviews and their associated constraints. But if you are doing any manual configuration of frame values on the basis of the main view's dimensions, the configuration of those frames should be deferred until viewWillAppear or viewDidLayoutSubviews.

  • The viewWillAppear method is called when the presentation of the view in the view hierarchy is about to start. Notably, this is called at the start of the animation (if any) of the presentation of the view. Its companion, viewWillDisappear is obviously called when the transition away from this view begins.

  • The viewDidAppear method is called when the presentation of the view is done, notably when any and all associated animation has finished. Its companion, viewDidDisappear is obviously called when the transition away from this view is done.

Two important caveats:

  • viewDidLoad is called once and only once, when the view is first instantiated. On the other hand, viewWillAppear and viewDidAppear will be called not only when the view is first presented, but every subsequent time the same view in question is re-presented. For example, when you first present a view, all three of these methods will be called. If the view in question subsequently presents another view which is subsequently dismissed, the viewWillAppear and viewDidAppear will generally be called again when the view in question is added and animated back into the view hierarchy, but viewDidLoad will not. viewDidLoad is only called when this particular instance is first created.

    So, if you want to do something every time a view reappears (e.g. you dismiss or pop back to it), do it in viewWillAppear or viewDidAppear. If you want it to only happen when the view is first instantiated, do that in viewDidLoad.

  • The calling of viewWillAppear does not guarantee that transition to that view will ever be completed. Notably, if you are using interactive transition that are driven by real-time user input, but that interactive transition can be canceled. I.e., just because viewWillAppear is called, it does not mean that viewDidAppear will called. Generally it is, but if the interactive gesture is cancelled, it won't (because the transition never finished).

    At WWDC 2013, in the context of interactive transitions, a presenter joked that they should rename viewWillAppear to "viewMightAppear, or viewWillProbablyAppear, or iReallyWishThisViewWouldAppear".

    An example of a built-in interactive gesture is when using a UINavigationController and you "swipe from the left edge" to initiate a pop of the view. The viewWillAppear will be called for the view to which you are popping, but if you cancel that "swipe from left edge" to return back to the view from which you started this pop gesture, the pop is canceled and the viewDidAppear for the view you started to pop back to will never be called.

    The net effect of this is that you should be careful that you don't write code that assumes that every call to viewWillAppear will be followed eventually by a call to viewDidAppear. If the transition is canceled, this will not be the case.