viewDidLoad is called before whole init method is executed
Asked Answered
K

3

14

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.

Keese answered 18/7, 2015 at 9:58 Comment(0)
K
16

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.

Kind answered 18/7, 2015 at 11:27 Comment(3)
Thank you. But I think that this is inconsistent behaviour and is not well documented. I send TSI to Apple and I'm interested to see their answer.Keese
@Keese I'd love to hear what Apple has to say when they give you an answer, but I suspect that you won't get much additional information. This is a standard iOS behavior. You're right though regarding the lack of documentation for TabBarController subclassing.Kind
I wonder why would you do anything to a UIViewController's view on init ?Signac
A
13

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.

Actiniform answered 18/4, 2016 at 19:24 Comment(0)
S
3

It seems that init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) for a UITabBarController calls viewDidLoad for some reason. If you set a breakpoint on your print("viewDidLoad") line you will see that the call is made as part of the initialisation sequence.

If you change your view controller to subclass UIViewController you will see that viewDidLoad is not called as part of the initialisation sequence, but rather as a result of calling makeKeyAndVisible

I don't know why Apple coded it this way, but I suspect it is to give the tab bar controller an opportunity to set things up before the content view controllers are loaded.

Regardless, it is just something you are going to have to deal with if you want to subclass UITabBarController

Scandura answered 18/7, 2015 at 11:52 Comment(1)
You're right. I have come to the same conclusion. Even so, I'm curious about Apple engineers comment. I will post it here then.Keese

© 2022 - 2024 — McMap. All rights reserved.