Repository navigation
Conversation
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
Native responder and run-loop timing warrant human verification across supported iOS versions.
No open issues 🎉
What changed in this PR
Addresses the iOS counterpart of Android’s page-change focus fix, preventing off-screen inputs from retaining focus and keyboard input.
Changes:
- Defers focus cleanup after page selection while preserving focus on the selected page.
- Extends regression tests to iOS, including keyboard and refocus checks.
- Documents focus behavior and simulator keyboard setup.
| File | Description |
|---|---|
| README.md | Documents page-change blur behavior. |
| ios/PagerViewProvider.swift | Adds deferred responder cleanup and delegate forwarding. |
| example/src/gh-issues/Issue1154PageChangeFocusRepro.tsx | Updates the reproduction comment to cover both platforms. |
| e2e/tests/issues/issue-1154-page-change-focus.e2e.ts | Adds iOS keyboard assertions and swipe refocus coverage. |
| e2e/README.md | Documents simulator software-keyboard setup. |
Review effort: Balanced
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.
Summary
Fixes #1192, the iOS half of #1154. Android was fixed in #1155 and shipped in 9.0.8. iOS kept the old behaviour, so the same code blurs on one platform and not the other.
On iOS, a
TextInputfocused on a page keeps the first responder after the pager moves to another page. The keyboard stays up, nothing on the new page is focused, and keystrokes go into a field that is no longer on screen. The iOS implementation never touches focus, and SwiftUI's pagedTabViewkeeps the neighbouring pages in the view hierarchy, so nothing takes the responder away.Screen.Recording.2026-10-19.at.10.36.23.PM.mov
Screen.Recording.2026-10-19.at.10.31.14.PM.mov
Test Plan
When a page is selected,
PagerViewProviderresigns the first responder of any page other than the selected one. The search is scoped to the pages, so a responder belonging to the pager itself is left alone, and only a responder that takes text is resigned: a view that doesn't conform toUIKeyInputand has noinputView, such as one holding key commands, is not an input.The blur is scheduled rather than performed inline. Scheduling it on the next run-loop pass does three things:
onPageSelectedhandler focused, though the keyboard can close and reopen on the way.The keyboard therefore goes as soon as the page change is committed, and a gesture commits when the finger lets go, which is the same split
UIScrollView.keyboardDismissModemakes between.onDragand.interactive.The selected page is read when the blur fires, so a second page change that arrives first decides which page keeps its input, and a selection naming a page that hasn't mounted yet blurs nothing until it has. A pending blur is cancelled when the pager leaves the window, since it retains the pager until it fires.
The delegate forwarding moved into a small private observer object, so that conforming to the
@objcprotocol doesn't make those methods public.What's required for testing (prerequisites)?
bun install,bun podsinexample/, thenbun example:ios:release.Simulator.app'sConnectHardwareKeyboardpreference doesn't reach a device the e2e engine booted itself. Clear the device's own flag after building, since the flag can be re-marked during a build and launch:xcrun simctl spawn booted defaults write com.apple.keyboard.preferences HardwareKeyboardLastSeen -bool falseThis is in
e2e/README.mdnow.issue-1096-keyboard-shrinkdepends on it too.What are the steps to reproduce (after prerequisites)?
Automated, from
e2e/:issue-1154-page-change-focuscovers four cases, each checking the selected page, the focused input and the keyboard: leaving a page withsetPageblurs its input, leaving it with a swipe blurs its input, an input focused right aftersetPagekeeps focus, and a swipe keeps the focus the new page takes fromonPageSelected.On an iPhone 18 Pro (iOS 27), 9.0.9 with React Native 0.88:
Selected: 0 | Focused: input 1 | Keyboard: visibleaftersetPage, andSelected: 1 | Focused: input 0 | Keyboard: visibleafter a swipe. The other two pass, since nothing interferes with them before the fix.By hand, in the example app under Issue #1154 Page Change Focus Repro:
Before: the status line stays on
Focused: input 1 | Keyboard: visibleand the characters land in the input on the page you left. After: it readsFocused: none | Keyboard: hiddenas the pager settles, withblur 1, keyboardHidein the event log.Compatibility
Checklist
README.md