Repository navigation
Add useBarRegion for the iOS 27.1 vertical bar layout region - #43
Draft
janicduplessis wants to merge 1 commit into
Draft
janicduplessis wants to merge 1 commit into
janicduplessis wants to merge 1 commit into
Conversation
Report where UIKit places bar content of a given extent, such as the iPhone Duo status column, relative to the nearest provider. The provider passes the requested extents to the native view, which measures them in the same pass as divisions and occlusions. Android and iOS before 27.1 return null.
|
Perfect thx |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
On iPhone Duo (iOS 27.1) the system shows status in a vertical column on the trailing edge. Native bars put controls in that column: the back button and tab bar items are placed from UIKit's bar layout region (
UIView.LayoutRegion.bar(onEdge:extent:), ObjClayoutRegionForBarOnDirectionalEdge:extent:). React Native apps that want the same thing, such as a header back button or a map's location button, have no way to find that spot.The occlusion regions this library already reports don't answer it. They only say where content is hidden (the camera and the status area at the top of the column). The bar region says where controls go: it starts below the status area, is centered on the status icons, and depends on the size of the content placed in it.
Measured on the Duo simulator, iOS 27.1, full-screen provider:
399.7, 29.3, 37×37, status area382, 0, 84×174396, 174, 44×470394, 174, 48×470867, 0, 84×124881, 124, 44×511879, 124, 48×511API
The frame is relative to the nearest
ReservedRegionsProvider, unclipped, like the other regions. It returnsnullwhen no vertical bar overlaps the provider, on Android, when built with an SDK older than iOS 27.1 (compiled out with the same__IPHONE_27_1guard as the existing calls) and on iOS older than 27.1.I kept the name
useBarRegion. It follows UIKit'sLayoutRegion.bar, and it is not a reserved region kind, so it stays out ofuseReservedRegions().Native design
The region depends on the extent, so each hook registers its extent with the provider (ref-counted, so hooks asking for the same extent share one query). The provider passes the list to the native view as a
barExtentsprop, and the native view measures every extent in the samelayoutSubviewspass as divisions and occlusions and sends them in the sameregionsChangeevent (bars: [{ extent, edge, frame }],edge: 'none'when there is no bar). During a fold, regions and bars therefore always come from the same layout pass and arrive together.I ruled out the other two shapes:
Re-queries happen on layout, hinge updates (existing), changes to
UITraitCollection.systemTraitsAffectingVerticalBarEdge(new, viaregisterForTraitChanges), and when the extent list changes. UIKit posts no notification for the region itself.Window, not provider. The native view queries the bar region on its window and converts it to provider coordinates. Querying on the provider itself is wrong whenever the provider doesn't line up with the window, because UIKit computes a view's bar region from that view's own bounds and safe area. Folded Duo, extent 44, both results converted to window coordinates:
0, 0, 466×678396, 174, 44×470396, 174, 44×47040, 0, 466×678396, 174, 44×470378, 17.3, 44×626.724, 24, 418×554396, 174, 44×470372, 174, 44×40424, 180, 418×398396, 174, 44×470372, 180, 44×398That is the same mismatch th3rdwave saw on a nested tab screen (x = 254). The bar is reported when the window's region overlaps the provider, the same rule
reservedRegionsOfKind:uses.Readiness. A new extent is measured one layout pass after the hook first requests it, so the first render returns
nulleven on a Duo.useReservedRegionsReadyandReservedRegionsGateare unchanged and don't wait for bar regions. This is documented in the API reference.Android needs no native change: the view manager ignores the unknown
barExtentsprop, and a missingbarsfield reads as no bar.Verification
Example app: a teal overlay for the 44-point region, a round 44-point button placed in it that counts taps, and text lines for the 44 and 48 extents.
396, 174, 44×470and394, 174, 48×470, matching the measurements above. Shift 40 / Inset 24 / Content box report the same column in provider coordinates (356, 174,372, 150,372, -6). Unfolded reports881, 124, 44×511. Half-open (130°) reports the division plus the same unfolded bar region. Folding open → half-open → closed returns to the folded values. Tapping the bar button on the folded Duo increments the counter.Bar 44: none,Bar 48: none, no button.Bar 44: none,Bar 48: none, measurement Ready (release with the corner cutout overlay, which still reports its occlusion).none, and the outside-provider error.e2e/android.mjsnow also asserts both bar lines readnone.Not verified
UIVerticalBarEdgeLeading, so the leading branch is untested.UIVerticalBarBehaviorDisabled: I assume the window region is empty then and the hook returnsnull, but the example doesn't set it.iosCI job builds on themacos-26runner with its default Xcode, which should exercise it.pnpm run e2e:androidend to end: the release build installed, but on this heavily loaded host the emulator showed system ANR dialogs and the script's snapshot ran before the app was in front, and the API 34 image reported no hinge division. The new assertion is only checked by hand (screenshot above). Thee2e-androidlabel on this PR runs it in CI.main. Once it lands it can assert the folded bar region.