Skip to content

fix(ios): clear focus and hide the keyboard when leaving a page - #1193

Open
TaduJR wants to merge 2 commits into
callstack:mainfrom
TaduJR:fix/ios-clear-focus-when-leaving-a-page
Open

TaduJR wants to merge 2 commits into
callstack:mainfrom
TaduJR:fix/ios-clear-focus-when-leaving-a-page

Conversation

@TaduJR

@TaduJR TaduJR commented Oct 19, 2026 •

Copy link
Copy Markdown

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 TextInput focused 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 paged TabView keeps the neighbouring pages in the view hierarchy, so nothing takes the responder away.

Before (9.0.9) After
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, PagerViewProvider resigns 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 to UIKeyInput and has no inputView, 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:

  • Resigning inside the selection change forces a synchronous layout pass that applies the change outside its animation.
  • Scheduling it in default mode holds it for the whole of a swipe, because the gesture and the deceleration that follows keep the run loop in tracking mode, so the keyboard is never dismissed under the user's finger.
  • Because the blur runs after the event, it never takes the focus from an input an onPageSelected handler 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.keyboardDismissMode makes between .onDrag and .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 @objc protocol doesn't make those methods public.

What's required for testing (prerequisites)?

  • bun install, bun pods in example/, then bun example:ios:release.
  • A simulator that shows its own keyboard. A device that has seen a Mac keyboard once shows none again, across boots, and Simulator.app's ConnectHardwareKeyboard preference 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 false

This is in e2e/README.md now. issue-1096-keyboard-shrink depends on it too.

What are the steps to reproduce (after prerequisites)?

Automated, from e2e/:

bun run test:ios -- --tag issue-1154

issue-1154-page-change-focus covers four cases, each checking the selected page, the focused input and the keyboard: leaving a page with setPage blurs its input, leaving it with a swipe blurs its input, an input focused right after setPage keeps focus, and a swipe keeps the focus the new page takes from onPageSelected.

On an iPhone 18 Pro (iOS 27), 9.0.9 with React Native 0.88:

  • with this patch, five consecutive runs, 20 of 20
  • on an unpatched build of the same commit, three runs, the two blur cases fail every time: Selected: 0 | Focused: input 1 | Keyboard: visible after setPage, and Selected: 1 | Focused: input 0 | Keyboard: visible after 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:

  1. Tap the input on page 1 and wait for the keyboard.
  2. Tap Go 0, or swipe back to page 0.
  3. Type.

Before: the status line stays on Focused: input 1 | Keyboard: visible and the characters land in the input on the page you left. After: it reads Focused: none | Keyboard: hidden as the pager settles, with blur 1, keyboardHide in the event log.

Compatibility

OS Implemented
iOS ✅
Android ✅

Checklist

  • I have tested this on a device and a simulator
  • I added the documentation in README.md
  • I updated the typed files (TS and Flow)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 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

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.

[iOS] Focused TextInput keeps focus and keyboard after switching pages (v9)

2 participants