|
| 1 | +In this article let's look at the life cycle of a ViewController. We'll see when methods are called and what you can do inside them. We'll also look at common errors. |
| 2 | + |
| 3 | +Let's start with the `UIView`. The memory is allocated during initialization, so the behavior is predictable. Now the properties have values and the object can be used. |
| 4 | + |
| 5 | +The controller has a view. But just because the controller is created, does not mean that the view is created too. The system is waiting for a reason to create it. The lifecycle concept is built around this feature. Just keep in mind that the view is created by necessity. |
| 6 | + |
| 7 | +## Initializing |
| 8 | + |
| 9 | +Consider the basic `UIViewController` which has two initializers: |
| 10 | + |
| 11 | +```swift |
| 12 | +override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { |
| 13 | + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) |
| 14 | +} |
| 15 | + |
| 16 | +required init?(coder: NSCoder) { |
| 17 | + super.init(coder: coder) |
| 18 | +} |
| 19 | +``` |
| 20 | + |
| 21 | +There is also an initializer without parameters `init()`, but this is a wrapper over the first initializer. |
| 22 | + |
| 23 | +At this point, the controller behaves like a class: it initializes the property and handles the initializer body. The controller may be in a condition without a loaded view for a long time, or it may never even load one. The view will load as soon as the system or the developer accesses the `.view' property. |
| 24 | + |
| 25 | +## Loading |
| 26 | + |
| 27 | +The developer presents the controller. The memory is allocated because the system loads the view. We can follow the process and even intervene. Let's see what methods are available: |
| 28 | + |
| 29 | +```swift |
| 30 | +override func loadView() {} |
| 31 | +``` |
| 32 | + |
| 33 | +The `loadView()` method is called by the system. You don't need to call it manually but you can override it to replace the root view. If you need to load the view manually (and you know what you're doing), hold down the red `loadViewIfNeeded()` button. |
| 34 | + |
| 35 | +> `super.loadView()` не нужно. |
| 36 | +
|
| 37 | +The second method is legendary like Steve Jobs. It is called when the view has finished loading. |
| 38 | + |
| 39 | +```swift |
| 40 | +override viewDidLoad() { |
| 41 | + super.viewDidLoad() |
| 42 | +} |
| 43 | +``` |
| 44 | + |
| 45 | +There is a reason why developers set up the controller and views in the `viewDidLoad()` method. Before this method is called, the root view doesn't exist yet, and afterward, the controller is ready to appear on the screen. The `viewDidLoad()` is a great place. The memory for the view is allocated, the view is loaded and ready to be set up. |
| 46 | + |
| 47 | +The view cannot be configured in the initializer. When you invoke `.view', it will load, but the controller won't show up on the screen now (or may not show up at all). The project will not crash from this, but the interface elements consume a lot of memory and it will be spent earlier than necessary. It is better to do this as needed. |
| 48 | + |
| 49 | +Previously I made property views of the controller just by creating them: |
| 50 | + |
| 51 | +```swift |
| 52 | +class ViewController: UIViewController { |
| 53 | + |
| 54 | + var redView = UIView() |
| 55 | +} |
| 56 | +``` |
| 57 | + |
| 58 | +The property is initialized with the controller, which means the memory for the view is allocated immediately. To hold off this you need to mark the property as `lazy`. |
| 59 | + |
| 60 | +In the `viewDidLoad()` method, the size of the view is wrong, so you can't bind to height and width. Do a setting that does not depend on size. |
| 61 | + |
| 62 | +I wanna focus on `viewDidUnload()`. The root view can be unloaded from memory, which means something incredible: |
| 63 | + |
| 64 | +>The `viewDidLoad()` method can be called several times. |
| 65 | +
|
| 66 | +For example, if you close the modal controller, the view will be deallocated from memory, but the controller object will still be alive. If you show the controller again, the view will load again. If the system dumped the view, it means there was a reason. There is no need to refer to the root view in this method - it will cause it to load. Outlets are still available here, but are no longer meaningful - you can reset them. |
| 67 | + |
| 68 | +You don't have to rush to take off-hours and spend all weekend redoing your VPN. Nothing will break, `viewDidLoad()` is rarely called multiple times. Keep in mind that you need to split the configuration of data and views in your next project. |
| 69 | + |
| 70 | +## Showing |
| 71 | + |
| 72 | +The appearance of the controller begins with the `viewWillAppear` method: |
| 73 | + |
| 74 | +```swift |
| 75 | +override func viewWillAppear(_ animated: Bool) { |
| 76 | + super.viewWillAppear(animated) |
| 77 | +} |
| 78 | + |
| 79 | +override func viewDidAppear(_ animated: Bool) { |
| 80 | + super.viewDidAppear(animated) |
| 81 | +} |
| 82 | +``` |
| 83 | + |
| 84 | +Both methods are paired. You don't need to do any customization here, but you can hide/show views or add some simple behavior. In the `viewDidAppear()` method, start a network request or spin the load indicator. Both methods can be called multiple times. |
| 85 | + |
| 86 | +Some methods report that the view disappears from the screen. See the schematic: |
| 87 | + |
| 88 | + |
| 89 | + |
| 90 | +Note the two antagonists `viewWillDisappear()` and `viewDidDisappear`. They are called when the view is removed from the view hierarchy. If you show another controller on top, the methods are not called. |
| 91 | + |
| 92 | +## Layout |
| 93 | + |
| 94 | +Layout methods, similar to the methods above, are tied to the life cycle of the view. Three methods are available: |
| 95 | + |
| 96 | +```swift |
| 97 | +override func viewWillLayoutSubviews() { |
| 98 | + super.viewWillLayoutSubviews() |
| 99 | +} |
| 100 | + |
| 101 | +override func viewDidLayoutSubviews() { |
| 102 | + super.viewDidLayoutSubviews() |
| 103 | +} |
| 104 | +``` |
| 105 | + |
| 106 | +The first method is called before `layoutSubviews()` of the root view, the second method is called after. In the second method, the size is correct and the views are placed correctly - you can link to the size of the root view. |
| 107 | + |
| 108 | +There is a special method for resizing a view. With this method you can adjust the rotation of the device: |
| 109 | + |
| 110 | +```swift |
| 111 | +override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { |
| 112 | + super.viewWillTransition(to: size, with: coordinator) |
| 113 | +} |
| 114 | +``` |
| 115 | + |
| 116 | +The `viewWillLayoutSubviews()` and `viewDidLayoutSubviews()` methods will then be called. |
| 117 | + |
| 118 | +## Out of memory |
| 119 | + |
| 120 | +This method is called if the memory overflows. If you don't clear the objects that cause it, iOS will force the application to shut down (to the user, it will look like a crash). |
| 121 | + |
| 122 | +```swift |
| 123 | +override func didReceiveMemoryWarning() { |
| 124 | + super.didReceiveMemoryWarning() |
| 125 | +} |
| 126 | +``` |
| 127 | + |
| 128 | +That's all. Controller lifecycle is a big topic I might have missed something. Let me know if you find something or have a good example for an article. |
0 commit comments