Skip to content

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

Open

Description

@TaduJR

Environment

Description

This is the iOS half of #1154, which was fixed for Android only.

A TextInput that's focused on one page stays focused after you switch to another page, whether with setPage or a swipe. The keyboard stays open, nothing on the page you land on is focused, and typing still goes into the input that's now off-screen, so the user edits a field they can't see.

The iOS implementation never touches focus. PagerViewProvider forwards onPageSelected and leaves the first responder alone, and SwiftUI's paged TabView keeps the neighbouring pages in the view hierarchy, so the input that was left behind holds the first responder and the keyboard with it.

keyboardDismissMode doesn't cover this. It defaults to none, and even on-drag only reacts to a drag, so setPage never dismisses. Dismissing the keyboard is also not the same as blurring, which is what leaves keystrokes going into the off-screen field.

Android behaved the same way until #1155, which clears focus held outside the selected page when a page is selected, and hides the keyboard explicitly. The two platforms disagree today: the same code blurs on Android 9.0.8 and keeps focus on iOS.

Expected: leaving a page blurs an input focused on it and the keyboard follows, as it now does on Android.
Actual: the input keeps focus, the keyboard stays open, and keystrokes go to the off-screen page.

Here is the moment it goes wrong, with a status line showing the selected page, the focused input and the keyboard: the pager is on page 0, while page 1's input still holds focus and the keyboard.

Image

Found in Expensify/App#101157, where a pager hosts a form on each tab.

Reproducible Demo

import { useRef } from 'react';
import { Button, StyleSheet, TextInput, View } from 'react-native';
import PagerView from 'react-native-pager-view';

export function Repro() {
  const ref = useRef<PagerView>(null);

  return (
    <PagerView ref={ref} style={StyleSheet.absoluteFill} initialPage={0}>
      {[0, 1].map((page) => (
        <View key={page} style={{ flex: 1, padding: 24, gap: 12 }}>
          <TextInput placeholder={`Input ${page}`} style={{ borderWidth: 1, padding: 12 }} />
          <Button title={`Go to page ${1 - page}`} onPress={() => ref.current?.setPage(1 - page)} />
        </View>
      ))}
    </PagerView>
  );
}
  1. Focus Input 1 on page 1 and wait for the keyboard.
  2. Tap Go to page 0, or swipe back to page 0.
  3. Type.

The keyboard never closes, and the characters land in Input 1 while page 0 is on screen. Swipe back and you'll find the text in the input you couldn't see.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions