Skip to content

Commit

Permalink
Fix <KeyboardAvoidingView> with floating keyboard on iPad (#44859)
Browse files Browse the repository at this point in the history
Summary:
On iPadOS, users can change the kind of keyboard displayed onscreen, going from normal keyboard, to split keyboard (one half on the left of the screen, one half on the right), or a floating keyboard that you can move around the screen.

When a non-normal kind of keyboard is used, `<KeyboardAvoidingView>` calculations are all wrong and, depending on the `behavior` prop, can make your screen completely hidden.

This PR attempts to detect that the keyboard is not the "normal displayed-at-bottom-of-screen" keyboard, and forces `enable={false}` if this happens.

The approach of comparing the keyboard width with the window width comes from this comment: #29473 (comment)

A better fix might be to detect the kind of keyboard used, but this involves native code changes and I do not know iOS enough to do that. In addition, I have not found an easy way to do it using iOS APIs after a quick search.

I also chose to cache the window width as a class attribute. Maybe this is not needed as `Dimensions.get('window').width` is very fast and can be called on every keyboard event?

This fixes #44068 and #29473

## Changelog:

[IOS] [FIXED] - Fix `<KeyboardAvoidingView>` with floating keyboard on iPadOS

Pull Request resolved: #44859

Test Plan:
Tested using RNTester and the "Keyboard Avoiding View with different behaviors" example.

Before:

https://github.com/facebook/react-native/assets/42070/111598a3-286c-464d-8db8-73afb35cd7f9

After:

https://github.com/facebook/react-native/assets/42070/0b3bc94f-8b67-4f42-8a83-e11555080268

Reviewed By: cortinico

Differential Revision: D62844854

Pulled By: cipolleschi

fbshipit-source-id: 577444be50019572955a013969d78178914b5b8d
  • Loading branch information
renchap authored and facebook-github-bot committed Sep 23, 2024
1 parent a6fcad7 commit 3c54e1e
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/

import type {ViewStyleProp} from '../../StyleSheet/StyleSheet';
import type {DimensionsPayload} from '../../Utilities/NativeDeviceInfo';
import type {
ViewLayout,
ViewLayoutEvent,
Expand All @@ -18,6 +19,7 @@ import type {KeyboardEvent, KeyboardMetrics} from './Keyboard';

import LayoutAnimation from '../../LayoutAnimation/LayoutAnimation';
import StyleSheet from '../../StyleSheet/StyleSheet';
import Dimensions from '../../Utilities/Dimensions';
import Platform from '../../Utilities/Platform';
import {type EventSubscription} from '../../vendor/emitter/EventEmitter';
import AccessibilityInfo from '../AccessibilityInfo/AccessibilityInfo';
Expand Down Expand Up @@ -66,6 +68,7 @@ class KeyboardAvoidingView extends React.Component<Props, State> {
viewRef: {current: React.ElementRef<typeof View> | null, ...};
_initialFrameHeight: number = 0;
_bottom: number = 0;
_windowWidth: number = Dimensions.get('window').width;

constructor(props: Props) {
super(props);
Expand Down Expand Up @@ -130,6 +133,10 @@ class KeyboardAvoidingView extends React.Component<Props, State> {
}
};

_onDimensionsChange = ({window}: DimensionsPayload) => {
this._windowWidth = window?.width ?? 0;
};

// Avoid unnecessary renders if the KeyboardAvoidingView is disabled.
_setBottom = (value: number) => {
const enabled = this.props.enabled ?? true;
Expand All @@ -145,6 +152,15 @@ class KeyboardAvoidingView extends React.Component<Props, State> {
return;
}

if (
Platform.OS === 'ios' &&
this._windowWidth !== this._keyboardEvent.endCoordinates.width
) {
// The keyboard is not the standard bottom-of-the-screen keyboard. For example, floating keyboard on iPadOS.
this._setBottom(0);
return;
}

const {duration, easing, endCoordinates} = this._keyboardEvent;
const height = await this._relativeKeyboardHeight(endCoordinates);

Expand Down Expand Up @@ -178,6 +194,7 @@ class KeyboardAvoidingView extends React.Component<Props, State> {
if (Platform.OS === 'ios') {
this._subscriptions = [
Keyboard.addListener('keyboardWillChangeFrame', this._onKeyboardChange),
Dimensions.addEventListener('change', this._onDimensionsChange),
];
} else {
this._subscriptions = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1780,10 +1780,12 @@ declare class KeyboardAvoidingView extends React.Component<Props, State> {
viewRef: { current: React.ElementRef<typeof View> | null, ... };
_initialFrameHeight: number;
_bottom: number;
_windowWidth: number;
constructor(props: Props): void;
_relativeKeyboardHeight(keyboardFrame: KeyboardMetrics): Promise<number>;
_onKeyboardChange: $FlowFixMe;
_onLayout: $FlowFixMe;
_onDimensionsChange: $FlowFixMe;
_setBottom: $FlowFixMe;
_updateBottomIfNecessary: $FlowFixMe;
componentDidUpdate(_: Props, prevState: State): void;
Expand Down

0 comments on commit 3c54e1e

Please sign in to comment.