Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

viewDidLoad called before prepareForSegue finishes

I was under the impression that viewDidLoad will be called AFTER prepareForSegue finishes. This is even how Hegarty teaches his Stanford class (as recently as Feb 2013).

However, for the first time today, I have noticed that viewDidLoad was called BEFORE prepareForSegue was finished. Therefore, the properties that I was setting in prepareForSegue were not available to the destinationViewController within the destinations viewDidLoad method.

This seems contrary to expected behavior.

UPDATE

I just figured out what was going on. In my destinationViewController I had a custom setter that would reload the tableView each time the "model" was updated:

DestinationViewController     - (void)setManagedObjectsArray:(NSArray *)managedObjectsArray     {         _managedObjectsArray = [managedObjectsArray copy];         [self.tableView reloadData];     } 

It turns out, since the destinationViewController is a subclass of UITableViewController...calling 'self.tableView' forces the view to load. According to Apple's documentation, calling the view property of a view controller can force the view to load. The view of a UITableViewController is the tableView.

http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html

Therefore, in prepareForSegue the following line was forcing the view of the destinationViewController to load:

vc.managedObjectsArray = <custom method that returns an array>; 

To fix the problem, I changed the custom setter of the destinationViewController's model to:

- (void)setManagedObjectsArray:(NSArray *)managedObjectsArray     {         _managedObjectsArray = [managedObjectsArray copy];         if ([self isViewLoaded]) {             [self.tableView reloadData];         }     } 

This will only reload the tableView if the tableView is on screen. Thus not forcing the view to load during prepareForSegue.

If anyone objects to this process, please share your thoughts. Otherwise, I hope this prevents a long sleepless night for someone.

like image 500
Michael Guren Avatar asked Feb 28 '13 23:02

Michael Guren


People also ask

What is called before viewDidLoad?

Yes, viewDidLoad method is called before viewDidAppear:. viewDidLoad and viewDidLoad: mean actually different things. You specify : if it has an argument, but viewDidLoad does not, just as a convention. Loaded in memory means ready to use/display. Follow this answer to receive notifications.

Does viewWillAppear get called before viewDidLoad?

viewWillAppear(_:)Always called after viewDidLoad (for obvious reasons, if you think about it), and just before the view appears on the screen to the user, viewWillAppear is called.

How many times does viewDidLoad get called?

viewDidLoad() is one of the initialization methods that is called on the initial view controller. viewDidLoad() is called before anything is shown to the user - and it is called only once.


2 Answers

I had run into similar confusions in the past. The general rules of thumb that I learned are:

  1. prepareForSegue is called before destination VC's viewDidLoad
  2. viewDidLoad is called only after ALL outlets are loaded
  3. Do not attempt to reference any destination VC's outlets in source VC's prepareForSegue.

Another lesson learned with regards to prepareForSegue is to avoid redundant processing. For example, if you already segue a tableView cell to a VC via storyboard, you could run into similar race condition if you attempt to process BOTH tableView:didSelectRowAtIndexPath and prepareForSegue. One can avoid such by either utilizing manual segue or forgo any processing at didSelectRowAtIndexPath.

like image 173
Hampden123 Avatar answered Sep 21 '22 15:09

Hampden123


Thought I'd add a little explanation on what happened here:

prepareForSegue is called before the view is displayed

viewDidload is called the first time the view is accessed (view is lazy loaded).

What probably happened is that you accessed the view in prepareForSegue triggering the view loading manually.

Usually the flow is:

  1. preformSegue
  2. prepareForSegue
  3. ViewController is added to the hierarchy
  4. viewDidLoad.

But what probably happened in your case is:

  1. preformSegue
  2. prepareForSegue
  3. |— In prepareForSegue you access the view
  4. |— view is automatically loaded => viewDidLoad is invoked
  5. Return from performSegue
  6. ViewController is added to the hierarchy, no viewDidLoad (already called)

So yes usually the view property is not accessed before the ViewController is added to the hierarchy, but if it is, viewDidLoad can be triggered earlier.

like image 45
Antzi Avatar answered Sep 19 '22 15:09

Antzi