UI Part 1 (UIViewController LifeCycle)

Ali Akhtar
10 min readApr 5, 2020

--

https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/WorkWithViewControllers.html

UIViewController

An object of the UIViewController class (and its subclasses) comes with a set of methods that manage its view hierarchy. iOS automatically calls these methods at appropriate times when a view controller transitions between states. When you create a view controller subclass (like the ViewController class you’ve been working with), it inherits the methods defined in UIViewController and lets you add your own custom behavior for each method.

loadView

  1. Each view controller manages a view hierarchy, the root view of which is stored in the view property of UIViewController class. The root view acts primarily as a container for the rest of the view hierarchy. This method creates/loads the view that the controller manages. Primary purpose of this method is to load a view and assign it to view property of the UIViewController instance which acts as a root view
  2. You can override this method in order to create your views manually. We will see this later As per apple “If you prefer to create views programmatically, instead of using a storyboard, you do so by overriding your view controller’s loadView method.
  3. You should never call this method directly. The view controller calls this method when its view property is requested but is currently nil. This method loads or creates a view and assigns it to the view property. So never calls this method
  4. If you use Interface Builder to create your views and initialize the view controller, you must not override this method.
  5. Don’t perform any additional initialization of your views, do so in the viewDidLoad().
  6. Do setting up the view hierarchy in this method only when you are creating view manually not from any interface builder
  7. loadView either needs to call [super loadView] or you need to create a view and assign it to self.view. If the method doesn’t do one of those things the view controller will not have a view -> black screen.
  8. When the view property of a UIViewController is accessed, a simplified pseudocode for returning a view looks something like this.
--> If there’s already a view set, return that view--> Call loadView--> The default implementation of loadView in UIViewController does something like this--> If there’s an associated storyboard / xib,
--> Load the view hierarchy from the storyboard / xib
--> Assign the root view to the view property
--> If there’s no associated storyboard / xib
--> Create a UIView instance with a default frame and autoResizingMask
--> Assign that view to the view property
--> For any layout guides created from a storyboard / xib, setup --> --> Auto Layout constraints between the layout guides and the view of the view controller
--> Call viewDidLoad

Let’s dive into code

We created a LoginViewController from storyboard and we only override loadView method nothing more. As shown in Figure 1 we saw a black screen

Figure 1

As shown in Figure 2 we removed loadView override method which fix the problem. We can also fix this by calling super.loadView in loadView method . A view controller has an associated nib file if the nibName property returns a non-nil value, which occurs if the view controller was instantiated from a storyboard, if you explicitly assigned it a nib file using the initWithNibName:bundle: method, or if iOS finds a nib file in the app bundle with a name based on the view controller’s class name. If the view controller does not have an associated nib file, this method creates a plain UIView object instead.

Figure 2

As shown in Figure 3 we created only CustomViewController which don’t have any UI in the storyboard here we manually created root view.

You can override this method in order to create your views manually. If you choose to do so, assign the root view of your view hierarchy to the view property. The views you create should be unique instances and should not be shared with any other view controller object. Your custom implementation of this method should not call super.

Figure 3

loadViewIfNeeded

If in case the view of current viewController has not been set yet then this method will load the view but remember, this is only available in iOS >=9.0. So if you are supporting iOS <9.0 then don't expect it to come into the picture.

Loads the view controller’s view if it has not already been set.

ViewDidLoad

  1. The viewDidLoad event is only called when the view is created and loaded into memory but the bounds for the view are not defined yet. This is a good place to initialise the objects that the view controller is going to use.
  2. Called after the view has been loaded. For view controllers created in code, this is after -loadView. For view controllers unarchived from a nib, this is after the view is set.
  3. By the time viewDidLoad is called, all of the IBOutlets—assuming they’re actually connected to something in the corresponding IB file—should be non-nil. Means we can access outlets in ViewDidLoad. They likely don’t have a relevant size or position yet.
  4. viewDidLoad is things you have to do once. It calls once in a life cycel of a UIViewController
  5. You should not initialise UI geometry-related things in viewDidLoad, because the geometry of your view is not set at this point and the results will be unpredictable.
  6. At the time viewDidLoad is called, the view Controller view has been loaded, but it has not yet been inserted into the interface, the view has not been fully resized for the first time , and initial layout has not yet taken place. You cannot do anything here that depends upon the knowing the dimensions of the view controller’s view or any other nil-loaded view — for the simple reason you don’t know them. performing layout related tasks in viewDidLoad and then wondering why things are sized or positioned incorrectly is a common beginner mistake.

As shown in Figure 4 on ViewDidLoad we got a wrong frame. Doing calculation on this you can imagine what can happen. Actual fram of the button when everything placed is x = 129, y= 183.0 , width =46 , height = 39

Figure 4

Why it is giving 1024 width because I used ViewTraits for iPad and Iphone currently my interface builder view is in iPAd which has different constraint than iphone. So in ViewDidLoad and viewWillAppear it is showing the interface builder structure not actually the device where it is running which is iPhone 11

Figure 4.1

By changing the View to Iphone 11 in interface builder I am getting the frame correct because device that is running is the same where we developed our interface

Figure 4.2

As shown in Figure 5 we created a label in Viewdidload by using the btn frame which gives wrong value on ViewdidLoad. Since btn is layout properly on relevant methods by autolayout engine but our label don;t

Figure 5

The natural place to put geometry related code is an event related to resizing and the layout of the main view. such as viewWillLayoutSubviews ut we need to be careful since this method is called multiple times over the life of a viewController that’s why we create UILabel one time at viewDidLoad and update its layout only in this method super cool

Figure 6

The second solution is to configure it for future layout by giving autolayout constraint on viewDidLoad . At this point constraint are not layout they are instructed as how this view should be resized and positioned by the runtime when layout does happen

https://www.amazon.com/Programming-iOS-13-Controllers-Frameworks-ebook/dp/B082FS7STT

ViewWillAppear

  1. viewWillAppear gets called every time the view appears. Is called only once each time the view is added to the view hierarchy
  2. Dismissing a modal with presentation Style = .fullScreen also called ViewWillAppear on the presented View Controller. Most modal presentations in your app will default to a sheet presentation in iOS 13. In this case it will not call
  3. Notifies the view controller that its view is about to be added to a view hierarchy.
  4. If a view controller is presented by a view controller inside of a popover, this method is not invoked on the presenting view controller after the presented controller is dismissed.
  5. As of iOS 10, the bounds are correct in viewWillAppear.
  6. When using autolayout in ios6, frames are not set until subviews have been laid out and in this method you will get the wrong frame as shown in Figure 4 and 5
  7. viewWillAppear: is not call when we rotate out device and the app was backgrounded and is becoming the foreground app
  8. Don’t forget to call super on any of the lifecycle methods of the UIViewController
  9. This can call repeatedly, because your viewcontroller can go away and come back .

viewWillLayoutSubviews

This is the first step in the lifecycle where the bounds are finalised. If you are not using constraints or Auto Layout you probably want to update the subviews here. Called to notify the view controller that its view is about to layout its subviews

viewDidLayoutSubviews:

This event notifies the view controller that the subviews have been setup. It is a good place to make any changes to the subviews after they have been set. it only take places after all the auto layout or auto resizing calculations on the views have been applied. Meaning the method viewDidLayoutSubviews is called every time the view size changes and the view layout has been recalculated. Called to notify the view controller that its view is laid off

  1. These get called every time the controller’s view is updated, rotated, or changed (or more technically, each time its bounds change)
  2. View controller can override this method to make changes before and after the view lays out its subviews.
  3. We can’t do geometry things in viewWillAppear because we saw previously that geometry was not set at this point. In viewDidAppear you can do this but at that point you are too late and you are on screen. So to place any geometry in viewWillLayoutSubviews and viewDidLayoutSubviews methods. These two methods are sent to your controller when it’s view (self.view) just before and just after
  4. Usually we don’t do anything in this method because we use an auto layout
  5. These can be called multiple times so be prepared, or there is also a possibility that it gets called twice with the same bounds. So don’t do any expensive things in there

As shown in Figure 7 , initially we are in portrait mode , when we rotate device to landscape mode these methods get called and when this happens you get layoutSubViews things because your bounds change but you also get this animation for free . So iOS automatically animates the moving of your subviews from portrait layoutSubview to landscape layoutSubview

Figure 7

As shown in Figure 5 we solved the layout problem by using viewWillLayoutSubviews .

viewDidAppear

The viewDidAppear event fires after the view is presented on the screen. Which makes it a good place to get data from a backend service or database.

  1. Called when the view has been fully transitioned onto the screen
  2. This Method is called after the view present on the screen. Usually save data to core data or start animation or start playing a video or a sound, or to start collecting data from the network This type of task good for this method.
  3. Called after viewDidLayoutSubviews
  4. Outlets , bounds are all set
  5. Means view is appeared on screen now and we can see as well
  6. Notifies the view controller that its view was added to a view hierarchy.

viewWillDisappear

The viewWillDisappear event fires when the view of presented viewController is about to disappear, dismiss, cover or hide behind other viewController.

  1. This is a good place where you can restrict your network calls, invalidate timer , remove observer or release objects which is bound to that viewController.
  2. Notifies the view controller that its view is about to be removed from a view hierarchy.

viewDidDisappear

This is the last step of the lifecycle that anyone can address as this event fires just after the view of presented viewController has been disappeared, dismissed, covered or hidden.

  1. Called after the view was dismissed, covered or otherwise hidden. Default does nothing
  2. Notifies the view controller that its view was removed from a view hierarchy.

Useful Links

https://www.amazon.com/Programming-iOS-13-Controllers-Frameworks/dp/1492074616

https://www.amazon.com/Programming-iOS-13-Controllers-Frameworks-ebook/dp/B082FS7STT

--

--

Ali Akhtar
Ali Akhtar

Written by Ali Akhtar

Senior iOS Engineer | HungerStation | Delivery Hero

Responses (1)