Skip to content

fix(ios): stop the GeometryReader root from shrinking pages for the keyboard - #1150

Merged
troZee merged 2 commits into
callstack:masterfrom
seantanys:fix/ios-keyboard-shrinks-pages
Sep 29, 2026
Merged

troZee merged 2 commits into
callstack:masterfrom
seantanys:fix/ios-keyboard-shrinks-pages

Conversation

@seantanys

@seantanys seantanys commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Related to #1099 (keyboard variant, see comment). Follow-up to #1096, which regressed in 9.0.2.

On iOS, pages shrink natively when the keyboard opens, while React Native still lays them out at full height. Anything in the lower part of a page is clipped, and its native frame no longer matches the React Native layout, so taps miss it. onLayout keeps reporting the full height, so nothing on the JS side can compensate.

original

Before (9.0.5) After (this PR)
pager-before-fix.mp4
pager-after-fix.mp4

reproduction

Before (9.0.5) After (this PR)
pager-repro-unfixed.mp4
pager-repro-fixed.mp4

What happened

Fix

Apply .ignoresSafeArea() to the GeometryReader root as well. Layout is owned by React Native, so the pager should not react to the keyboard at all. The inner modifier on the TabView stays, so the vertical-layout frames are unchanged.

       }
     }
+    .ignoresSafeArea()
     .onAppear {

PageChildViewController.propagateSafeArea() reads insets from the RN screen view, not from the hosting view, so child UIKit insets (#1085, #1142) are unaffected.

Test Plan

What's required for testing (prerequisites)?

iOS simulator or device, New Architecture. A pager inside a container that follows the keyboard with a transform (the common KeyboardStickyView / chat-composer setup). When the pager fills the screen the shrunk area sits under the keyboard, so the bug is invisible there.

What are the steps to reproduce (after prerequisites)?

Added Issue #1096 Keyboard Shrink Repro to the example app and .maestro/issues/issue_1096_keyboard_shrink_repro.yaml:

  1. Open the example. A bottom sheet holds a five-row pager and a text input; the sheet lifts with the keyboard.
  2. Focus the input.
  3. Before: once the keyboard settles, only Row 1 is left and the rest of the page is empty. Row 5's native frame reports y 574pt while React Native lays it out at 372pt, so the Maestro tap lands on the keyboard (last-row taps: 0).
  4. After: all five rows stay visible and Row 5 increments the counter, with the keyboard open and after it is dismissed.

Verified

  • bun typescript, bun lint and bun test pass.
  • The new Maestro flow fails on 9.0.5 without the fix and passes with it (iPhone 17 Pro simulator, iOS 26.2, Release build).
  • Full iOS Maestro suite with the fix: 10/12 pass. pager_rtl_example and pager_vertical_basic_example fail in the layout-direction setup step, before any pager code runs: DevSettings.reload() does not take effect in the Release build, so the direction never flips. pager_vertical_basic_example passes when run on its own. RTL is not verified.

Found in a production app (chat mention picker built on react-native-tab-view): on 9.0.5 only the first row of a page survived with the keyboard open; with this change the page matches React Native's layout. Android is untouched.

Compatibility

OS Implemented
iOS ✅
Android n/a

Checklist

  • I have tested this on a device and a simulator: simulator only (iPhone 17 Pro, iOS 26.2), not on physical hardware
  • I added the documentation in README.md: not applicable
  • I updated the typed files (TS and Flow): not applicable

…eyboard

Since callstack#1101 the GeometryReader is the root of PagerView.body and every page
is framed to its proxy size. It still honoured the keyboard safe-area region,
so pages shrank by the keyboard overlap while React Native kept them at full
height. Apply .ignoresSafeArea() to the root as well.

Adds the Issue callstack#1096 Keyboard Shrink Repro example and a Maestro flow.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@seantanys

Copy link
Copy Markdown
Contributor Author

Heads-up: #1149 touches the same spot. It adds .ignoresSafeArea(isVertical ? .all : [], edges: .all) to the GeometryReader root, which covers the vertical case but is a no-op for horizontal pagers (the default, and the case here), so the keyboard shrink stays. The two will conflict on that line. Options:

cc @ArekChr

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The targeted fix is consistent with the SwiftUI layout hierarchy and is covered by a focused regression flow.

Review effort: Balanced
Findings: None

What changed in this PR

Prevents iOS SwiftUI keyboard avoidance from shrinking React Native-managed pager pages.

Changes:

  • Makes the root GeometryReader ignore safe areas.
  • Adds an iOS keyboard-shrink reproduction screen and Maestro regression test.
  • Registers the reproduction in the example app.
File Description
ios/​PagerView.swift Prevents keyboard-driven page resizing.
example/​src/​gh-issues/​Issue1096KeyboardShrinkRepro.tsx Adds the reproduction screen.
example/​src/​App.tsx Registers the new example.
.maestro/​setup/​issue_1096_keyboard_shrink_repro_setup.yaml Opens the reproduction screen.
.maestro/​issues/​issue_1096_keyboard_shrink_repro.yaml Verifies interaction before and after keyboard dismissal.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@troZee
troZee enabled auto-merge (squash) September 29, 2026 14:29
@troZee
troZee merged commit 52a4264 into callstack:master Sep 29, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants