fix(ios): propagate safe area insets to child UIKit views inside PagerView#1085
Open
IsaacIsrael wants to merge 1 commit into
Open
fix(ios): propagate safe area insets to child UIKit views inside PagerView#1085IsaacIsrael wants to merge 1 commit into
IsaacIsrael wants to merge 1 commit into
Conversation
…rView SwiftUI's `.ignoresSafeArea()` modifier strips `safeAreaInsets` from all child UIKit views embedded via `UIViewRepresentable`. This prevents `contentInsetAdjustmentBehavior` from working on scroll views inside pager pages — the scroll view sees `safeAreaInsets = .zero` even when the hosting controller correctly reports the real device insets (e.g. bottom: 83pt for a tab bar on iOS 26 liquid glass). Fix: replace `UIViewRepresentable` with `UIViewControllerRepresentable`. The new `PageChildViewController` traverses up to `_UIHostingView`, reads its real `safeAreaInsets`, and re-injects them via `additionalSafeAreaInsets`. This restores proper safe area propagation to all descendant UIKit views (including `UIScrollView`), so `contentInsetAdjustmentBehavior` works as expected. Also adds `.ignoresSafeArea()` to the SwiftUI `TabView` body so page content extends visually behind system chrome (tab bars, etc.), and removes the `ignoreSafeArea: true` parameter from `UIHostingController` init so the hosting view preserves real device insets for `PageChildViewController` to read. Co-authored-by: Cursor <cursoragent@cursor.com>
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.
Problem
When using
react-native-pager-viewinside a screen with iOS safe area (e.g. behind a translucent tab bar on iOS 26 liquid glass), child UIKit views — includingUIScrollView/FlashList— receivesafeAreaInsets = .zero. This causescontentInsetAdjustmentBehaviorto have no effect: scroll views cannot automatically adjust their content insets to account for overlapping system chrome.Root cause
The current implementation uses
UIViewRepresentableto embed React Native views inside SwiftUI pages. TheUIHostingControlleris initialized withignoreSafeArea: true, which dynamically subclasses the hosting view to overridesafeAreaInsets→.zero. Even withoutignoreSafeArea, SwiftUI's layout system does not propagate safe area insets to child UIKit views embedded viaUIViewRepresentable.Debug evidence (from native logging added during investigation):
Solution
Three targeted changes:
Extensions.swift— ReplaceUIViewRepresentablewithUIViewControllerRepresentable. The newPageChildViewControllertraverses up to_UIHostingView, reads its realsafeAreaInsets, and re-injects them viaadditionalSafeAreaInsets. This restores proper safe area propagation to all descendant UIKit views.PagerView.swift— Add.ignoresSafeArea()to the SwiftUITabViewbody so page content extends visually behind system chrome (translucent tab bars, etc.), enabling the "scroll behind glass" effect.PagerViewProvider.swift— RemoveignoreSafeArea: truefromUIHostingControllerinit so the hosting view preserves real device insets forPageChildViewControllerto read.How it works
The guard with 0.5pt threshold prevents infinite layout loops when
viewDidLayoutSubviewssetsadditionalSafeAreaInsets.Test plan
contentInsetAdjustmentBehavior='scrollableAxes'works on scroll views inside PagerView pages (last item should stop above the tab bar when scrolled to bottom)Made with Cursor