Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

viewDidLoad is called before whole init method is executed

EDIT: Here is whole code example for Xcode 6.4

I have simple iOS application without storyboards. I set rootViewController for UIWindow in AppDelegate.swift like this:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    let tabBarController = TabBarController()

    window = UIWindow(frame: UIScreen.mainScreen().bounds)
    window?.rootViewController = tabBarController
    window?.makeKeyAndVisible()

    return true
}

TabBarController class implementation is as follows:

class TabBarController: UITabBarController {

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)

        // Next line is called after 'viewDidLoad' method
        println("init(nibName: bundle:)")
    }

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        println("viewDidLoad")
    }

}

When I run application the console output looks like this:

viewDidLoad
init(nibName: bundle:)

It means that lines after line super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) are called after viewDidLoad method! This occurs only for classes that inherits from UITabBarController. If you try this same example with UIViewController descendant, everything is ok and viewDidLoad is called after init method is executed.

like image 769
Deny Avatar asked Jul 18 '15 09:07

Deny


2 Answers

You are not guaranteed to have viewDidLoad to be called only after the init method is done. viewDidLoad gets called when a view-controller needs to load its view hierarchy.

Internally, TabBarController's init method (by calling super.init) is doing something which is causing the view to load.

This applies to all view-controllers. For example: if you create a UIViewController subclass and do anything with its view property on init, like adding a subview, or even just setting the backgroundColor property of the view - you will notice the same behavior.

like image 84
Artal Avatar answered Oct 22 '22 10:10

Artal


From: http://www.andrewmonshizadeh.com/2015/02/23/uitabbarcontroller-is-different/

This should come as no surprise, but apparently UITabBarController has a different behavior than most view controllers. The life cycle may overall be the “same” between it and other view controllers, but the order it executes is not.

That is, when you create a subclass of UITabBarController and provide your own custom initializer, you will notice that the viewDidLoad method is called in an unexpected way. That is, as soon as you call [super init] (or other initializer on UITabBarController), it will call loadView during that initialization which will then lead to your viewDidLoad being called. This is likely not what you would expect because most (all?) other UIViewController subclasses do not instantiate their view during the initialization process. As such, if you provided a custom initializer and expected to do some setup before the view is loaded, and then once the view is loaded add your contained view controllers, this will break your logic.

The solution is to actually move your setup code out of the standard viewDidLoad method and into a special setup method that is called at the end of your custom initializer. This strikes me as a “code smell” that Apple should have never let through. Likely though, this is because the UITabBarController needs to add a UITabBar to the UIViewController’s view which requires that the view exist. Which is what fires loadView.

like image 40
coolbeet Avatar answered Oct 22 '22 11:10

coolbeet