fix(ios): keep pages out of the safe area again - #1140
Conversation
callstack#1085 dropped `ignoreSafeArea: true` from the hosting controller when it added `propagateSafeArea()`. The two solve different problems: the flag keeps SwiftUI from laying the pages out inside the safe area, while `propagateSafeArea()` hands child UIKit views their insets back. With the flag gone, `PagerView`'s `GeometryReader` is measured inside the safe area and every page is framed to that measurement, so the pages shrink and shift while React Native's layout still has them at full size. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Based on this comment, it is not fixed yet #1142 (comment) |
|
@R4M80MrX Please use this repro for fixing it https://github.com/iliapnmrv/react-native-pager-view-ios-searchbar-inset/blob/main/App.tsx |
Restoring ignoreSafeArea: true keeps GeometryReader from shrinking the pages, but nearestNonZeroSafeAreaInsets() then stops at an inner SwiftUI host or the window. Those sources have the home-indicator inset and miss a UINavigationController search bar, which is why callstack#1142's bottom recovered and the top did not. Read PagerViewProvider.safeAreaInsets instead — that view is still under the screen view controller. Co-authored-by: Cursor <cursoragent@cursor.com>
|
@troZee I used iliapnmrv's repro. The first commit only restored
This follow-up leaves @iliapnmrv Could you re-test this branch with |
|
Hi! Unfortunately, the latest commit didn’t change anything for me. The bottom inset is fixed, but there is still an extra inset at the top |
PageChildViewController is under the hosting controller, which is a child of the RN screen, so it already inherits the nav + search-bar top inset. additionalSafeAreaInsets is additive: copying the pager's own insets (often top 0, because RN already laid the pager below the header) left that inherited extra in place, which is why callstack#1142 still showed a gap after the previous commit. Set additional to target - inherited — negative is required — so the page matches the pager's overlap with the screen safe area.
|
@iliapnmrv Thanks for re-testing with
Pushed another commit that sets Please rebuild natively once more ( |
|
Tested as well in my production app, it works, thank you! |
|
@iliapnmrv @R4M80MrX Once it is added, I will merge it |
troZee asked for the repro under ghIssues plus a Maestro check before merge. The example is iliapnmrv's native-stack search-bar case, and the flow asserts the pager list reports safe-area: pass from the first-row / bottom-marker window positions.
|
@troZee Added.
Please run |
There was a problem hiding this comment.
🟢 Approval recommended
The iOS safe-area change is coherent with the stated root cause, and the added example + Maestro regression flow provides targeted verification coverage for the new behavior.
Pull request overview
This PR restores iOS page layout to fill the full React Native–allocated frame by reintroducing ignoreSafeArea: true on the SwiftUI hosting controller, while updating safe-area propagation logic so embedded UIKit views inside pages still receive correct insets (including native navigation/search bar cases). It also adds a concrete example + Maestro regression flow for the related search-bar inset scenario.
Changes:
- Re-add
ignoreSafeArea: truewhen creating theUIHostingControllerto prevent SwiftUI safe-area from shrinking theGeometryReadermeasurement used to size pages. - Update
PageChildViewController.propagateSafeArea()to compute target insets based on pager overlap with the screen safe area and apply them viaadditionalSafeAreaInsets(including allowing negative adjustments). - Add an example repro screen and Maestro regression flow for the “native-stack stacked search bar + FlatList in PagerView” inset behavior.
File summaries
| File | Description |
|---|---|
| ios/PagerViewProvider.swift | Restores ignoreSafeArea: true and triggers relayout on safe-area changes so pages can recompute injected insets. |
| ios/Extensions.swift | Reworks safe-area propagation to derive correct target insets from pager/screen overlap and apply via additionalSafeAreaInsets. |
| example/src/gh-issues/Issue1142SearchBarInsetRepro.tsx | Adds a self-contained repro comparing plain vs PagerView-wrapped FlatList inset behavior. |
| example/src/App.tsx | Registers the new #1142 repro entry and adds two navigation routes for the control/repro screens. |
| .maestro/setup/issue_1142_search_bar_inset_repro_setup.yaml | Adds a shared setup flow to navigate to the #1142 pager repro screen. |
| .maestro/issues/issue_1142_search_bar_inset_repro.yaml | Adds an iOS-tagged regression flow asserting the repro reports safe-area: pass and paging still works. |
Review details
- Files reviewed: 6/6 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Summary
#1085 dropped
ignoreSafeArea: truefrom the hosting controller when it introducedpropagateSafeArea(). Restoring it fixes pages being laid out inside the safe area on iOS, and does not undo what #1085 set out to fix.The two mechanisms are independent:
ignoreSafeArea: true(disableSafeArea()) keeps SwiftUI from shrinking and shifting the pages.PagerView'sGeometryReaderis measured inside the hosting view and each page is framed toproxy.size, so a hosting view that carries a safe area produces pages inset by it.propagateSafeArea()onPageChildViewControllergives child UIKit views their insets back — the problem fix(ios): propagate safe area insets to child UIKit views inside PagerView #1085 was written for ([iOS] New Arch: safe area insets are stripped from PagerView children #1090, [iOS] v8 (SwiftUI TabView): pages shrink when the keyboard appears — real devices only #1096).With the flag gone, the convenience initialiser it used,
UIHostingController(rootView:ignoreSafeArea:), anddisableSafeArea()became dead code; nothing in the repo calls them onmaintoday.What it looks like
Measured on an iPhone 17 Pro Max simulator (iOS 26.5, 440×956pt, safe area top 62 / bottom 34), New Architecture,
react-native-pager-view9.0.4, React Native 0.86.3. The app places the pager above an 83pt React Native tab bar, so React Native lays the pager out at y0..873.I put coloured borders on the views and read their positions off a screenshot:
PagerViewitself0 .. 8730 .. 872.7✅0 .. 872.7✅0 .. 87331.0 .. 810.7❌0 .. 872.7✅The pages lost ~93pt and sat 31pt low. Anything anchored to the bottom of a page — in this app a right-hand action rail and the author/caption row — was pushed off screen.
onLayoutinside a page still reported the full 873pt, so nothing on the JS side could compensate for it.Relation to #1099
#1099 reports the same family of symptom on 8.0.4 and also points at #1085, but diagnoses a different mechanism:
propagateSafeArea()sampling_UIHostingView<_ViewList_View>'s bottom inset mid-relayout, while it flickers 49 → 24.33, and pinning the transient value.This change is not a fix for that sampling race, and I could not reproduce #1099's "corrects itself after the first scroll" behaviour — what I see is stable from the first render onwards. Zeroing the outer hosting view's insets may also remove an unstable source from the
nearestNonZeroSafeAreaInsets()walk, but the view named in #1099 is an inner hosting view for the page list, whichdisableSafeArea()does not subclass. That part is worth a maintainer's eye.Test Plan
What's required for testing (prerequisites)?
An iOS device or simulator with a home-indicator safe area, New Architecture, and a pager that is not itself inset — for example full-screen content behind a custom React Native tab bar.
What are the steps to reproduce (after prerequisites)?
<PagerView>filling a screen whose top and bottom reach into the safe area.position: 'absolute', bottom: 0).onLayouton the page reports the full height.Verified in the app this came from: the page container went from
31.0 .. 810.7to0 .. 872.7with no other change. The diff is iOS-only, so Android is untouched.Compatibility
Checklist
README.md— not applicable