How does a view controller know when the reserved regions change?

I've looked at the UIKit docs for UIView reservedRegions(kind:options:). I've watched the video "Strike a pose with adaptive layouts on iPhone Duo".

So I know how to get the current reserved regions. But I can't find the APIs to know when the reserved regions change. This means I can setup my custom view controller with the reserved regions that exist when the view controller is first displayed. But if the user changes the Duo, say from fully open to partially open, what API is triggered that lets me know that I need to call reservedRegions(kind:options:) and update the layout as needed?

I played around with UIHingeInteraction but that isn't the right approach. In most cases I tried calling reservedRegions(kind:options:) on each call to the interaction handler but I almost always got the wrong results (maybe a simulator issue?). And of course that interactiion handler will be called a lot just to report the new fold angle. I also tried various UIViewController methods for size changes and registered for size class changes. None of these were appropriate.

I just need a single notification that the Duo went from fully open to partially open, partially open to fully open, and partially open between landscape/portrait so that I can call reservedRegions(kind:options:) and get the correct, current divider info.

What am I missing?

My use case is for a custom fullscreen view controller that needs to layout a few items based on whether the Duo is fully open or partially open and whether the fold is horizontal or vertical. Unless I'm mistaken this precludes the use of UIArrangementViewController since I only have one view controller with one set of content to display. Showing the view controller as a form sheet, page sheet, or popover won't work since I need this to be fullscreen. I just need the fullscreen content to adjust for any fold (reserved region).

Answered by Pokpong in 911428022

I found a directly relevant answer from an Apple DTS engineer in the iPhone Duo UIKit Q&A: https://developer.apple.com/forums/thread/847876

For a custom UIKit layout, Apple's current guidance is to read reservedRegions(kind:options:) in updateConstraints(). UIKit tracks the regions read there and calls updateConstraints() again when they become active or inactive, so you can update the constraints from that method. The UIView.ReservedRegion docs show this pattern: https://developer.apple.com/documentation/uikit/uiview/reservedregion

If you need to trigger custom frame-based layout, use UIHingeInteraction to observe the hinge status, compare the new status with the previous one, and request a layout only when the pose changes. The interaction reports angle updates too, so filtering to status changes avoids doing work for every angle update. Then query the regions in your layout pass.

Accepted Answer

I found a directly relevant answer from an Apple DTS engineer in the iPhone Duo UIKit Q&A: https://developer.apple.com/forums/thread/847876

For a custom UIKit layout, Apple's current guidance is to read reservedRegions(kind:options:) in updateConstraints(). UIKit tracks the regions read there and calls updateConstraints() again when they become active or inactive, so you can update the constraints from that method. The UIView.ReservedRegion docs show this pattern: https://developer.apple.com/documentation/uikit/uiview/reservedregion

If you need to trigger custom frame-based layout, use UIHingeInteraction to observe the hinge status, compare the new status with the previous one, and request a layout only when the pose changes. The interaction reports angle updates too, so filtering to status changes avoids doing work for every angle update. Then query the regions in your layout pass.

@Pokpong Thanks, using updateViewConstraints is just what I needed. I had seen that other forum message you mention but the answer from the Apple engineer never actually said how to observe changes in UIKit, only how to get the current reserved regions.

I've also been looking at the documentation in Xcode. That documentation does not have nearly the same info for UIView.ReservedRegion that is ahown at https://developer.apple.com/documentation/uikit/uiview/reservedregion. If I had seen the online documentation I would have known the answer already. So strange the developer documentation in Xcode is so much less thorough than the online documentation.

Note that the very helpful sample code shown online for UIView.ReservedRegion only appears when selecting Swift. It does not appear when selecting Objective-C.

Reserved regions are observation tracked using the Swift Observation framework. Additionally, UIKit performs observation tracking for a number of methods (full list). You can read the reserved regions in, for example, layoutSubviews of your UIView subclass, and UIKit will automatically call setNeedsLayout and schedule a layout pass when the reserved regions change.

UIHingeInteraction generally should not be used for layout.

If you need to run code immediately when the reserved regions change, you can use an Swift Observation APIs like withObservationTracking and read the reserved regions from the apply closure.

@Frameworks Engineer Thanks. I'm actually working on a long-standing Objective-C app and the documentation for the Objective-C side of these things makes no mention of the observation tracking nor does it have any of the sample code shown in the Swift version of the documentation. But I've done some tests and reading the current reserved regions is working great inside updateViewConstraints in my Objective-C code.