Understanding Viewisappearing Key Update In Ios17 Uiviewcontroller
Understanding viewIsAppearing: A Key Update in iOS 17’s UIViewController
Created on: June 24, 2024 Last updated:
The basic life cycle of UIViewController consists of these three overridden methods:
- viewWillAppear(_:)
- viewDidAppear(_:)
- viewWillDisappear(_:)
- viewDidDisappear(_:)
Starting with iOS 17, this list is one method longer:
- viewWillAppear(_:)
- viewIsAppearing(_:)
- viewDidAppear(_:)
- viewWillDisappear(_:)
- viewDidDisappear(_:)
The viewIsAppearing method is back-deployed to iOS 13.
What says Documentation
The system calls this method once each time a view controller’s view appears after the viewWillAppear(_:)
call. In contrast to viewWillAppear(_:)
, the system calls this method after it adds the view controller’s view to the view hierarchy, and the superview lays out the view controller’s view. By the time the system calls this method, both the view controller and its view have received updated trait collections and the view has accurate geometry.
You can override this method to perform custom tasks associated with displaying the view. For example, you might use this method to configure or update views based on the trait collections of the view or view controller. Or, because computing a scroll position relies on the view’s size and geometry, you might programmatically scroll a collection or table view to ensure a selected cell is visible when the view appears.
Choosing the appropriate callback
Although the system calls this method after viewWillAppear(_:)
, both callbacks occur within the same CATransaction
. This means that changes you make in either method become visible to the user at the same time.
The traits and geometry aren’t up to date when the system calls viewWillAppear(_:)
, but they are when the system calls viewIsAppearing(_:)
, so use viewIsAppearing(_:)
to update your views.
Use viewWillAppear(:_)
only when:
-
You need a callback before the view transition begins, such as when accessing the
transitionCoordinator
to add alongside animations. Alongside animations are animations that you direct the framework to perform concurrently with the view controller transition animations. -
You need balanced callbacks to do something that doesn’t depend on the view controller or view traits, hierarchy, or geometry. Use cases include registering for database notifications in
viewWillAppear(_:)
and unregistering inviewDidDisappear(_:)
.
For all other cases, use viewIsAppearing(_:)
to update your views.
The system calls layout methods, such as viewWillLayoutSubviews()
and viewDidLayoutSubviews()
, whenever the view runs layoutSubviews()
, which can happen multiple times during the transition, or at any time while the view is visible. However, the system calls viewIsAppearing(_:)
only once during the appearance transition, and calls it even if the view doesn’t require laying out when it appears.
Table Choosing the appropiate callback in UIViewController
Callback | view.window | view.superview | view.traitsCollection up to date | view.safeAreaInsets up to date | view.frame up to date | view.subviews frames are up to date | Recursive subviews of view.subviews frames are up to date | |
---|---|---|---|---|---|---|---|---|
loadView | false | false | false | false | false | false | false | false |
viewDidLoad | false | false | false | false | false | false | false | false |
viewWillAppear | false | true | false | false | true | false | false | false |
updateViewConstraints | true | true | true | true | true | true | false | false |
viewIsAppearing | true | true | true | true | true | true | false | false |
viewWillLayoutSubviews | true | true | true | true | true | true | false | false |
viewDidLayoutSubviews | true | true | true | true | true | true | true | false |
viewDidAppear | true | true | true | true | true | true | true | true |
viewWillDisappear | true | true | true | true | true | true | true | true |
viewDidDisappear | false | true | true | true | true | true | true | true |
Personal Experience
-
When documentation says that view has size up to date in viewIsAppearing(_:) method it does not mean that first level subviews or more generaly recoursive subiews of the view were layouted.
-
SafaAreaInsets are up to date in viewIsAppearing(_:) method which is good news for all those logic which depended on it when positioning for example floting bottom buttons, buttons with different distances to bottom of screen depending if screen is with or without notch.
-
Starting a custom loaders loading animations in viewIsAppearing(_:) works contrary to starting them in viewDidLoad()
Known Issues
Due to the fact that the method is new and secondly back deployed to iOS 13, it’s possible to have some issues
There are two know issues, first one is viewIsAppearing not be called in children Controllers below iOS 16 posted on Apple’s forum and second one is Why is KeyboardLayoutGuide not applied in viewIsAppearing() in iOS 15.0? posted on StackOverflow.
Conclusions
viewIsAppearing can be a place for UI code which previously was present in viewWillAppear, updateViewConstraints, and viewDidAppear. To choose an appropriate presentation or layout callback, use the table introduced in the section ‘Table Choosing the appropriate callback in UIViewController’ by finding out the first callback which offers data needed for custom UI logic. Due to known issues, the code in viewIsAppearing should be well tested on iOS 16 and below. Important: viewIsAppearing, like other presentation callbacks, is executed only one time per presentation, but a view controller can be presented multiple times without being deinitialized, leading to code in those functions being called multiple times. To execute code only on the first-time call, use the CodeCallTracker framework.
Execute code block on first Call in viewWillAppear function
Use GitHub/Adobels/CodeCallTracker to execute a block of code only on first execution of any method. It works great with UIViewController presentation and layout callbacks to execute a code for example in first call of viewWillAppear like that:
class ViewController: UIViewController {
private let codeTracker = CodeCallTracker()
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated: animated)
if codeTracker.isFirstTimeCall() {
// do something only on first call of viewWillAppear
} else {
// do something on other calls of viewWillAppear
}
}
override func viewIsAppearing(animated: Bool) {
super.viewIsAppearing(animated: animated)
if codeTracker.isFirstTimeCall() {
// do something only on first call of viewIsAppearing
} else {
// do something on other calls of viewIsAppearing
}
}
Writing Xibs and Storyboards like SwiftUI
Use Github/Adobels/UIViewKit framwork to write your views like that:
class ViewController: UIViewController {
var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
view {
UILabel().ibOutlet(&label).ibAttributes {
$0.centerXAnchor.constraint(equalTo: view.centerXAnchor)
$0.centerYAnchor.constraint(equalTo: view.centerYAnchor)
$0.text = "Hello, world!"
}
}
}
}