React-native: KeyboardAvoidingView collapses to 0 height on iOS14 when "Prefer Cross-Fade Transitions" is enabled

Created on 18 Sep 2020  路  11Comments  路  Source: facebook/react-native

Description

For users with Prefer Cross-Fade Transitions enabled in their Accessibility > Motion options on iOS 14 KeyboardAvoidingView will collapse to 0px in height when the keyboard hides (when an input is blurred).

This only happens on physical devices as the Simulator doesn't have this accessibility option available.

The KeyboardAvoidingView behaves correctly when Prefer Cross-Fade Transitions is switched off.

The issue appears to be caused by incorrect endCoordinates coming back from the Keyboard.addListener('keyboardWillChangeFrame') listener in KeyboardAvoidingView as the values that come back differ significantly depending on the status of Prefer Cross-Fade Transitions.

This behaviour happens with behaviour set to height, padding and margin and doesn't appear to be an issue with the KeyboardAvoidingView directly but just the Keyboard events.

React Native version:

Tested in React Native 61.5 and 0.63.2 along with Expo v38


System information

System:
    OS: macOS 10.15.6
    CPU: (12) x64 Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz
    Memory: 837.72 MB / 32.00 GB
    Shell: 5.7.1 - /bin/zsh
  Binaries:
    Node: 14.5.0 - /var/folders/fj/y1w1yb3n4810ly8x70sds2d00000gn/T/yarn--1600411348330-0.8419068030637213/node
    Yarn: 1.22.5 - /var/folders/fj/y1w1yb3n4810ly8x70sds2d00000gn/T/yarn--1600411348330-0.8419068030637213/yarn
    npm: 6.14.5 - ~/.asdf/installs/nodejs/14.5.0/bin/npm
    Watchman: 4.9.0 - /usr/local/bin/watchman
  Managers:
    CocoaPods: 1.9.3 - /Users/levi/.asdf/shims/pod
  SDKs:
    iOS SDK:
      Platforms: iOS 14.0, DriverKit 19.0, macOS 10.15, tvOS 14.0, watchOS 7.0
    Android SDK: Not Found
  IDEs:
    Android Studio: 4.0 AI-193.6911.18.40.6626763
    Xcode: 12.0/12A7209 - /usr/bin/xcodebuild
  Languages:
    Java: 1.8.0_242-release - /usr/bin/javac
    Python: 3.8.5 - /Users/levi/.asdf/shims/python
  npmPackages:
    @react-native-community/cli: Not Found
    react: 16.13.1 => 16.13.1
    react-native: 0.63.2 => 0.63.2
    react-native-macos: Not Found
  npmGlobalPackages:
    *react-native*: Not Found

Steps To Reproduce

  1. On a device with iOS 14
  2. Enable Settings > Accessibility > Motion > Reduce Motion and Prefer Cross-Fade Transitions (will only show up after enabling Reduce Motion)
  3. Use a KeyboardAvoidingView in an app and observe the behaviour when dismissing a keyboard by blurring a TextInput

Expected Results

The KeyboardAvoidingView should reset to its normal height.

Snack, code example, screenshot, or link to a repository:

Snack: https://snack.expo.io/@levibuzolic/ios14-keyboard-avoiding-view

Minimal React Native Project: https://github.com/levibuzolic/KeyboardAvoidingView

Screenshots

Demonstrates the behaviour of the above Snack and React Native project on iOS 14 on an iPhone 11 Pro. The green border is inside the KeyboardAvoidingView to highlight the height of the content.

bug

Keyboard KeyboardAvoidingView Needs iOS

Most helpful comment

Here's the patch we're applying with patch-package against RN 63.2:

diff --git a/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js b/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
index 7c794ea..e3881ed 100644
--- a/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
+++ b/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
@@ -79,7 +79,9 @@ class KeyboardAvoidingView extends React.Component<Props, State> {

   _relativeKeyboardHeight(keyboardFrame): number {
     const frame = this._frame;
-    if (!frame || !keyboardFrame) {
+    // with iOS 14 & Reduce Motion > Prefer Cross-Fade Transitions enabled, the keyboard position
+    // & height is reported differently (0 instead of Y position value matching height of frame)
+    if (!frame || !keyboardFrame || keyboardFrame.screenY === 0) {
       return 0;
     }

Ultimately we'll want to fix this natively, but should be a safe patch for most as I can't think of many situations where we'd want a keyboard positioned at the top of the screen.

All 11 comments

I have same issue

Same issue here. Prefer Cross-Fade Transitions is enabled and it seems conflicting directly to the transition in the app.
ezgif-5-30226839b983

While we are waiting for a fix, we think about disabling the KeyboardAvoidingView for users that have isReduceMotionEnabled set to true. Maybe that's a quick solution for others as well, until the official fix is released.

You can read more about the accessibility info in the docs:
https://reactnative.dev/docs/accessibilityinfo#isreducemotionenabled

For what it's worth, I can reproduce this in the iOS simulator with 14, so an actual iOS 14 device isn't required.

Here's the patch we're applying with patch-package against RN 63.2:

diff --git a/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js b/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
index 7c794ea..e3881ed 100644
--- a/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
+++ b/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
@@ -79,7 +79,9 @@ class KeyboardAvoidingView extends React.Component<Props, State> {

   _relativeKeyboardHeight(keyboardFrame): number {
     const frame = this._frame;
-    if (!frame || !keyboardFrame) {
+    // with iOS 14 & Reduce Motion > Prefer Cross-Fade Transitions enabled, the keyboard position
+    // & height is reported differently (0 instead of Y position value matching height of frame)
+    if (!frame || !keyboardFrame || keyboardFrame.screenY === 0) {
       return 0;
     }

Ultimately we'll want to fix this natively, but should be a safe patch for most as I can't think of many situations where we'd want a keyboard positioned at the top of the screen.

Similar problem with Reduce Motion turned on with iOS 14.0 and Prefer Cross-Fade Transitions turned on with iOS 14.0.1

I'm having the same issue with react 0.61.5. Tried the patch but it didn't work.

@jdiaz-kindalab we're on 0.61.5 too and the patch is working for us. Are you sure it's being applied correctly?

Hopefully it's not. Here's my patch file:

diff --git a/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js b/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
index ae0dd37..3ee4458 100644
--- a/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
+++ b/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
@@ -82,7 +82,9 @@ class KeyboardAvoidingView extends React.Component<Props, State> {

   _relativeKeyboardHeight(keyboardFrame): number {
     const frame = this._frame;
-    if (!frame || !keyboardFrame) {
+    // with iOS 14 & Reduce Motion > Prefer Cross-Fade Transitions enabled, the keyboard position
+    // & height is reported differently (0 instead of Y position value matching height of frame)
+    if (!frame || !keyboardFrame || keyboardFrame.screenY === 0) {
       return 0;
     }

Would you mind sharing yours? To be clear, I'm having this issue even without Prefer Cross-Fade Transitions enabled. In my case, any tap I do outside the keyboard extends my screen back to normal height while keeping the keyboard open. It seems like the lost focus event is making the screen go back to normal size.

Thanks for checking out with me

I was able to reproduce this exact issue with an app in production after users wrote in about it. I will attempt one of the patches above to see if it resolves the issue and report back.

Can confirm @sterlingwes's fix works. Thank you!

Was this page helpful?
0 / 5 - 0 ratings