Skip to content

fix(ios): ignore keyboard events that don't originate from this app - #1512

Open
ridafkih wants to merge 2 commits into
kirillzyusko:mainfrom
ridafkih:fix/ignore-foreign-keyboard-events
Open

fix(ios): ignore keyboard events that don't originate from this app#1512
ridafkih wants to merge 2 commits into
kirillzyusko:mainfrom
ridafkih:fix/ignore-foreign-keyboard-events

Conversation

@ridafkih

@ridafkih ridafkih commented Jun 23, 2026

Copy link
Copy Markdown

📜 Description

💡 Motivation and Context

If you open an application into a screen that uses a KeyboardAvoidingView while a keyboard is open on iOS (ie., from Spotlight Search, swiping), despite iOS dismissing the keyboard, the area will be reserved for the keyboard because it does not ignore external keyboard observations.

📢 Changelog

iOS

  • ignore external keyboard movement observations

🤔 How Has This Been Tested?

In a local version, I applied the same changes to a patched version, and tested the changes.

📸 Screenshots (if appropriate):

Before After
ScreenRecording_06-23-2026.16-27-22_1.mov
ScreenRecording_06-23-2026.16-28-32_1.mp4

📝 Checklist

  • CI successfully passed
  • I added new mocks and corresponding unit-tests if library API was changed

@kirillzyusko

Copy link
Copy Markdown
Owner

Hey @ridafkih

Don't you mind attaching a video to show how it works before after? And maybe explain how to repro the bug? I. e. what exactly to do to repet the issue?

Comment thread ios/observers/movement/observer/KeyboardMovementObserver+Listeners.swift Outdated
@ridafkih

Copy link
Copy Markdown
Author

@kirillzyusko I'll get you a video, sounds good - for context the library iOS observer is reacting to all UIKeyboardWill/Did{Show,Hide}Notification without any filtering. So when the application foregrounds when there's a system keyboard from another context like iOS Spotlight (easiest way to reproduce imo) we observe the show with isLocal=false, and since there's no matching hide, it leaves the KAV stuck at a juicy keyboard-sized paddingBottom.

So to reproduce...

  1. Open a React Native application, and put it on a view with a KAV.
  2. Soft-close the application (background) on iOS
  3. Search for the application using Spotlight
  4. Click the "Return" or "Enter" key

From there, the KAV should have the padding still there.

@ridafkih

Copy link
Copy Markdown
Author

@kirillzyusko One important comment, I failed to reproduce the issue on a simulator. It requires--as far I can tell--a physical device.

@ridafkih

This comment was marked as duplicate.

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

📊 Package size report

Current size Target Size Difference
321429 bytes 321401 bytes 28 bytes 📈

@ridafkih

This comment was marked as duplicate.

@ridafkih

Copy link
Copy Markdown
Author

@kirillzyusko I've gone ahead and updated the description with videos of a reproduction and proof of patch.

@ridafkih
ridafkih requested a review from kirillzyusko June 23, 2026 20:51
@kirillzyusko kirillzyusko self-assigned this Jun 24, 2026
@kirillzyusko kirillzyusko added 🐛 bug Something isn't working 🍎 iOS iOS specific labels Jun 24, 2026
@kirillzyusko

Copy link
Copy Markdown
Owner

Hey @ridafkih - any ideas what I am doing wrong? I can not reproduce this issue using example app 🤔

Simulator.Screen.Recording.-.iPhone.17.Pro.-.2026-06-24.at.22.05.47.mov

@ridafkih

Copy link
Copy Markdown
Author

@kirillzyusko Not super sure, did you want the code for mine?

@ridafkih

Copy link
Copy Markdown
Author

Potential hint could be that I was also unable to replicate the issue in an Expo development client, it had to be a real build, and needed to be on an actual device.

@kirillzyusko

Copy link
Copy Markdown
Owner

Potential hint could be that I was also unable to replicate the issue in an Expo development client, it had to be a real build, and needed to be on an actual device.

This is from a real device:

ScreenRecording_06-25-2026.13-17-42_1.MP4

Not super sure, did you want the code for mine?

Yeah, feel free to share it 🙌 I would be happy to test it.


Basically I really want to merge this PR into main, but I'd like to have a reproduction repo/code/test scenario so that if any new bugs pop up because of this code I can easily adjust code and check that old bugs are not reproducible 🤞

@kirillzyusko

Copy link
Copy Markdown
Owner

I tested a little bit more and here is what I found. I tested it on iPad with multi-window setup and tested native apps (Messages/Safari):

Simulator.Screen.Recording.-.iPad.Pro.13-inch.M5.-.2026-06-25.at.18.21.44.mov

When Safari input gets a focus, then input inside Messages app still gets pushed up 🤔 With changes from this PR this will not happen (because we don't emit keyboard events if they are from the other app).

And at this point of time I'm not sure if it's a correct idea to simply ignore such events?.. It looks like native apps don't ignore it. Any ideas @ridafkih?

@ridafkih

ridafkih commented Jun 25, 2026

Copy link
Copy Markdown
Author

@kirillzyusko Will need to bear with me while I get reproduction up as a repository. In the meantime, this is virtually all the code for the one I showed in the video.

import { StatusBar } from 'expo-status-bar';
import {
  KeyboardAvoidingView,
  KeyboardProvider,
} from 'react-native-keyboard-controller';
import { SafeAreaView, StyleSheet, Text, View } from 'react-native';

export default function App() {
  return (
    <KeyboardProvider>
      <KeyboardAvoidingView behavior="padding" style={styles.kav}>
        <SafeAreaView style={styles.safe}>
          <View style={styles.content}>
            <Text style={styles.title}>RNKC Spotlight Repro</Text>
            <Text style={styles.body}>
              1. Background this app (swipe up).{'\n'}
              2. Open iOS Spotlight (swipe down on home screen).{'\n'}
              3. Foreground this app while Spotlight's keyboard is visible.
            </Text>
            <Text style={styles.body}>
              The pink gap below should not exist. It is
              KeyboardAvoidingView's paddingBottom, stuck at keyboard height.
            </Text>
          </View>
          <View style={styles.footer}>
            <Text style={styles.footerText}>Footer — pinned to bottom</Text>
          </View>
        </SafeAreaView>
      </KeyboardAvoidingView>
      <StatusBar style="auto" />
    </KeyboardProvider>
  );
}

const styles = StyleSheet.create({
  kav: { flex: 1, backgroundColor: '#ff3b9a' },
  safe: { flex: 1, backgroundColor: '#1d1d1f' },
  content: { flex: 1, padding: 24, gap: 16, justifyContent: 'center' },
  title: { color: 'white', fontSize: 28, fontWeight: '700' },
  body: { color: 'white', fontSize: 16, lineHeight: 22 },
  footer: {
    height: 80,
    backgroundColor: '#0a84ff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  footerText: { color: 'white', fontSize: 16, fontWeight: '600' },
});

A couple applications affected by this from your screen recording from your physical device are...

  • "Expensify" - which I was able to reproduce on the "What's your name?" screen on the onboarding.
  • "Twos" - seems to have a similar issue for the main view.

I've also replicated the issue on Strava (comment screen), LinkedIn on the main feed, etc., many applications experience a similar class of issue. Unsure if they use RNKC, but it's clear that this this is a regression for a lot of applications as of iOS 26.

I tested the behaviour prior to the change as well, and it doesn't seem to work particularly well regardless. In my opinion, this should be merged in so the immediate issue can be resolved - and then a follow up can be done to properly implement staging/windowed keyboard avoidance. I would be happy to assist with that.

Here is the video of the pre-patch variant running windowed on an iPad.

CleanShot 2026-06-25 at 12 47 16

@ridafkih

Copy link
Copy Markdown
Author

@kirillzyusko We could also flag this change, so that people can decide what to do with non-local events. I see you have some effort in-flight that might address this as well now.

@kirillzyusko

Copy link
Copy Markdown
Owner

In my opinion, this should be merged in so the immediate issue can be resolved - and then a follow up can be done to properly implement staging/windowed keyboard avoidance. I would be happy to assist with that.

The main issue is that this PR will break multi-window support. What I'm trying to say is that even native apps (that obviously don't use RNKC) push the input of other apps that don't have a focus at the moment (i. e. Safari + Messages).

If we start to ignore such events, we'll not follow the behavior of native apps?

The only difference that I see now is that native apps raise a view by a couple of pixels (not the full keyboard frame), while RNKC pushes view significantly higher. But at this point of time I think we need to re-work implementation of useWindowDimensions to support "floating" windows?

Does it make sense what I'm saying?

@ridafkih

Copy link
Copy Markdown
Author

@kirillzyusko Totally hear you, but I do think there might be an overindexing in regards to the importance of the iPad multi-window view. Not only is there a way for users using this mode to remediate the issue pending these changes by resizing and moving the window upwards (unlike their much more abundant single-window view counterparts), but the experience is quite degraded on the significantly more popular base-case of simply using the application on a device normally. As far as I can tell, other similar libraries have opted to ignore events in this exact case as well.

Thus I feel strongly that proceeding with a fix such as this as a first step is the right move, with more nuanced native-feeling approach later as a good second step.

If you feel strongly the other way, might I call back to my prior suggestion of allowing the developer to gate the change behind an option or a flag?

Otherwise feel free to close this pull request if you feel it has no path forward. :)

@ridafkih

Copy link
Copy Markdown
Author

Just to add, in windowed iPad view, the viewport does appear to be pushed up the entire keyboard height - not just a couple pixels. At least this is what I observed in my testing.

This means that, unfortunately, as the library is now: the experience is virtually unusable for small to medium sized windows on the iPad windowed view already and I'd argue this version is an improvement on that front.

@ridafkih

Copy link
Copy Markdown
Author

@kirillzyusko Feel free to close this if there's no plan to move forward, while I think this is a massive improvement for the UX that 99.9% of users are going to encounter, I understand not wanting to bring it in if it does not bring you closer to the goal of native-parity (even if it doesn't bring you any further, either).

This PR has been haunting my pulls queue for a few weeks now and I've already patched the bug out of all relevant projects that utilize RNKC under the hood.

@ridafkih

Copy link
Copy Markdown
Author

The only difference that I see now is that native apps raise a view by a couple of pixels

Not a couple pixels, but the exact number of pixels necessary to accommodate the keyboard. If the window is lower on the screen, it will adjust more than if it were at the top (likely none). That's the difference in behaviour.

Currently, RNKC unilaterally moves the keyboard the entire keyboard height up. This makes it so that, while the keyboard is up -- no matter it's position on the screen, it becomes virtually unusable depending on the size of the window. This is why I think this, despite being an incomplete change, is a good stop-gap until full native parity can be achieved from RNKC.

An application using RNKC that moves the keyboard up cannot really be effectively used when the keyboard is up on the screen, but an application using a monkey-patched version where external events do not trigger the keyboard to come up can simply move the window into view, then continue using the application. Hence why I think this is an improvement either way you swing.

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

Labels

🐛 bug Something isn't working 🍎 iOS iOS specific

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants