Search code examples
iosswiftuiviewuikituitraitcollection

Should I use viewDidLayoutSubviews() or traitCollectionDidChange() to respond to changes in interface orientation


I'm trying to update the constraints and change the layout of my subviews when the user rotates their device.

From what I understand, both viewDidLayoutSubviews() and traitCollectionDidChange() are able to respond to changes in the interface orientation (e.g. when the device is rotated).

According to Apple's documentation, for viewDidLayoutSubviews(),

When the bounds change for a view controller's view, the view adjusts the positions of its subviews and then the system calls this method.

Here I'm assuming that when the device gets rotated, the view's frame gets updated and the bounds gets updated at the same time as well.

As for traitCollectionDidChange(), I understand that the traitCollection property of UIViewController contains information of all the traits belonging to the current iOS environment, which includes not only traits like the horizontal and vertical size classes, but also traits like user interface style, which is in charge of setting dark and light mode.

Hence my question is, which method should I be using to respond to changes in the interface orientation? Is there any computational advantage of using one method over the other/which one is more architecturally sound?


Solution

  • Option C: viewWillTransition(to:with:) might be the better choice to update constraints. You can make a decision based on the new size and you can animate the changes by making use of the UIViewControllerTransitionCoordinator parameter. This will make the change in layout nice and smooth as part of the rotation animation.

    Keep in mind that you should not be focusing on the orientation. You should only focus on the view size of the view controller. Base all decisions on that size, not the orientation. By focusing on only the size, your views can better adapt to different devices and it can better adapt to multitasking on an iPad where the app may go from full screen to 2/3 screen to 1/2 screen, to 1/3 screen, all while the device orientation stays the same.


    Do note that traitCollectionDidChange(_:) may not even be called during an orientation change in some cases. Certain devices and certain iPad multitasking layouts will not result in either a vertical or horizontal class change when the device's orientation changes. But the view size may change just enough that you would want to adjust constraints but you won't be given the chance.


    The issue with viewDidLayoutSubviews is you end up changing the constraints after the rotation is complete and that leads to a less than clean change of layout. You can't use viewWillLayoutSubviews because you won't know the new size yet.