Skip to content

[iOS] Recycled InputAccessoryView is left visible and blocks touches on its TextInput (0.88 regression from #57590) #59354

Open

Description

@AjayFrancisTechversant

Description

On iOS (New Architecture / Fabric), a recycled InputAccessoryView component view is left visible (hidden = NO) and laid out inline on top of its TextInput. It swallows every touch, so the TextInput can no longer be focused: no caret, no keyboard. A freshly created InputAccessoryView is fine; only one dequeued from the recycle pool breaks.

This is a regression in 0.88.0. 0.87.1 is not affected.

Cause (from reading the sources):

  • RCTInputAccessoryComponentView hides itself only in updateProps: (self.hidden = true;).
  • RCTViewComponentView updateLayoutMetrics: passes its stored _layoutMetrics to UIView+ComponentViewProtocol, which does self.hidden = layoutMetrics.displayType == DisplayType::None when oldLayoutMetrics == EmptyLayoutMetrics (forceUpdate).
  • fix(ios): reset EmptyLayoutMetrics on Fabric view recycle #57590 (shipped in 0.88) changed RCTViewComponentView prepareForRecycle from _layoutMetrics = {}; to _layoutMetrics = EmptyLayoutMetrics;. A recycled view now takes the forceUpdate path on its first layout after reuse, which runs after updateProps: on mount and resets hidden to NO, undoing the accessory view's self.hidden = true.
  • InputAccessoryComponentDescriptor::adopt makes the node position: absolute with the viewport width, so the un-hidden view sits at the parent's top-left, full width, as tall as the accessory content, covering the TextInput.

RCTInputAccessoryComponentView.mm, UIView+ComponentViewProtocol.mm and LayoutMetrics.h are identical between 0.87.1 and 0.88.0; the only differing file on this path is RCTViewComponentView.mm. main still has the same code.

Possible fix (untested): re-assert the hidden state after layout in RCTInputAccessoryComponentView, e.g.

- (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics
           oldLayoutMetrics:(const LayoutMetrics &)oldLayoutMetrics
{
  [super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
  self.hidden = YES;
  [_contentView setFrame:RCTCGRectFromRect(layoutMetrics.getContentFrame())];
}

The same reset may affect other component views that hide themselves outside layout.

Real-world impact: any screen or modal with a multiline or number-pad TextInput + InputAccessoryView that is unmounted and mounted again in the same app session. For example, a verification-code field that turns non-editable while submitting (which unmounts/remounts its accessory view) can't be typed into again after a failed attempt.

Steps to reproduce

Using the RNTesterPlayground below (iOS, New Architecture):

  1. Tap the field. It focuses and the "Done" accessory bar appears. ✅
  2. Tap Unmount field, then Mount field.
  3. Tap the field again. Nothing happens: no caret, no keyboard. ❌

Expected: the field focuses every time.

Native inspection after step 2 shows the RCTInputAccessoryComponentView (nativeID playgroundAccessory) with hidden = NO, frame {24, 204, 402, 44}, directly over the TextInput ({25, 205, 352, 42}), and it is the hit-test winner at the field's centre. After step 1 the same view is hidden = YES.

import * as React from 'react';
import {useState} from 'react';
import {Button, InputAccessoryView, StyleSheet, Text, TextInput, View} from 'react-native';

const ACCESSORY_ID = 'playgroundAccessory';

function Playground() {
  const [visible, setVisible] = useState(true);
  return (
    <View style={styles.container}>
      <Button
        title={visible ? 'Unmount field' : 'Mount field'}
        onPress={() => setVisible(v => !v)}
      />
      {visible ? (
        <View style={styles.field}>
          <TextInput
            style={styles.input}
            keyboardType="number-pad"
            inputAccessoryViewID={ACCESSORY_ID}
            placeholder="Tap me"
          />
          <InputAccessoryView nativeID={ACCESSORY_ID}>
            <View style={styles.bar}>
              <Text>Done</Text>
            </View>
          </InputAccessoryView>
        </View>
      ) : null}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {padding: 24},
  field: {marginTop: 24},
  input: {height: 44, borderWidth: 1, borderColor: '#999', paddingHorizontal: 8},
  bar: {height: 44, alignItems: 'flex-end', justifyContent: 'center', paddingHorizontal: 16, backgroundColor: '#eee'},
});

React Native Version

0.88.0

Affected Platforms

Runtime - iOS

Output of npx @react-native-community/cli info

System:
  OS: macOS 27.0
  CPU: (8) arm64 Apple M2
  Memory: 90.34 MB / 16.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 24.16.0
  Yarn:
    version: 1.22.22
  npm:
    version: 11.13.0
  Watchman: Not Found
Managers:
  CocoaPods:
    version: 1.16.2
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 27.0
      - iOS 27.0
      - macOS 27.0
      - tvOS 27.0
      - visionOS 27.0
      - watchOS 27.0
  Android SDK: Not Found
IDEs:
  Android Studio: 2024.2 AI-242.23726.103.2422.13016713
  Xcode:
    version: 27.0/27A266a
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.14
    path: /usr/bin/javac
  Ruby:
    version: 2.6.10
    path: /usr/bin/ruby
npmPackages:
  "@react-native-community/cli":
    installed: 20.2.0
    wanted: 20.2.0
  react:
    installed: 19.3.0
    wanted: 19.3.0
  react-native:
    installed: 0.88.0
    wanted: 0.88.0
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: true
iOS:
  hermesEnabled: true
  newArchEnabled: true

Stacktrace or Logs

No crash, warning or log output. The TextInput silently ignores taps.

MANDATORY Reproducer

https://gist.github.com/AjayFrancisTechversant/53c0ab6ab267bf31d61b50b133f845ac

Screenshots and Videos

Reproduced on an iPhone 17 Pro simulator (iOS 27.0) with React Native 0.88.0, New Architecture, Hermes. The component tree above was run inside an existing 0.88.0 app rather than inside RNTester itself.

Activity

  1. react-native-bot commented on Oct 22, 2026

    @react-native-bot
    Collaborator

    Warning

    Missing reproducer: We could not detect a reproducible example in your issue report. Reproducers are mandatory and we can accept only one of those as a valid reproducer:


    You can read more about about it on our website: How to report a bug.

  2. AjayFrancisTechversant commented on Oct 22, 2026

    @AjayFrancisTechversant
    Author

    Reproducer PR (edited RNTesterPlayground.js): #59356

    Steps in RNTester → Playground on iOS (New Architecture): tap the field (works) → Unmount field → Mount field → tap the field again (no focus, no keyboard).

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

    Needs: AttentionIssues where the author has responded to feedback.Needs: ReproThis issue could be improved with a clear list of steps to reproduce the issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions