mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Fixes for Keyboard Observer, KeyboardAvoidingView on iOS (#48131)
Summary: **Convert keyboard position to window coordinate space on iOS** The keyboard frame passed to keyboardWillChangeFrame is in the screen's coordinate space [1]. It needs to be converted to the window's coordinate space to support Slide Over and Stage Manager. [1] https://developer.apple.com/documentation/uikit/uikeyboardframeenduserinfokey?language=objc |Before|After| |---|---| ||| **Improve detached keyboard detection on iOS** The iOS keyboard may be in one of three states: 1) floating (previously supported with a width check) 2) split 3) or undocked. In addition, when using Stage Manager, the keyboard may be wider than the window itself. This would cause the floating keyboard check to incorrectly set the bottom position to zero. Instead, rely on the fact that the UIKeyboardWillHideNotification notification is sent when the keyboard is in any detached state. This requires listening for UIKeyboardWillShowNotification instead of UIKeyboardWillChangeFrameNotification. This is fine, since the show notification is also sent when the keyboard resizes while open. Combined with the coordinate system adjustments in the previous commit, this also fixes an issue with Stage Manager, since the keyboard may be attached yet wider than the application window. |Before|After| |---|---| ||| ||| ||| ## Changelog: <!-- Help reviewers and the release process by writing your own changelog entry. Pick one each for the category and type tags: [ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message For more details, see: https://reactnative.dev/contributing/changelogs-in-pull-requests --> [iOS] [FIXED] - Keyboard events are converted to window coordinate space [iOS] [FIXED] - Improve detached keyboard detection, support Stage Manager on iOS Pull Request resolved: https://github.com/facebook/react-native/pull/48131 Test Plan: See screenshots above. Reviewed By: yungsters Differential Revision: D67337314 Pulled By: cipolleschi fbshipit-source-id: abe872ac8c83336f316917074f72cdb23a39caab
This commit is contained in:
committed by
Facebook GitHub Bot
parent
4141560afc
commit
c499ae1192
+14
-18
@@ -9,7 +9,6 @@
|
||||
*/
|
||||
|
||||
import type {ViewStyleProp} from '../../StyleSheet/StyleSheet';
|
||||
import type {DimensionsPayload} from '../../Utilities/NativeDeviceInfo';
|
||||
import type {
|
||||
ViewLayout,
|
||||
ViewLayoutEvent,
|
||||
@@ -19,7 +18,6 @@ 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';
|
||||
@@ -68,7 +66,6 @@ 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);
|
||||
@@ -115,6 +112,12 @@ class KeyboardAvoidingView extends React.Component<Props, State> {
|
||||
this._updateBottomIfNecessary();
|
||||
};
|
||||
|
||||
_onKeyboardHide = (event: ?KeyboardEvent) => {
|
||||
this._keyboardEvent = null;
|
||||
// $FlowFixMe[unused-promise]
|
||||
this._updateBottomIfNecessary();
|
||||
};
|
||||
|
||||
_onLayout = async (event: ViewLayoutEvent) => {
|
||||
event.persist();
|
||||
|
||||
@@ -135,10 +138,6 @@ 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;
|
||||
@@ -154,15 +153,6 @@ 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);
|
||||
|
||||
@@ -200,8 +190,14 @@ class KeyboardAvoidingView extends React.Component<Props, State> {
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
this._subscriptions = [
|
||||
Keyboard.addListener('keyboardWillChangeFrame', this._onKeyboardChange),
|
||||
Dimensions.addEventListener('change', this._onDimensionsChange),
|
||||
// When undocked, split or floating, iOS will emit
|
||||
// UIKeyboardWillHideNotification notification.
|
||||
// UIKeyboardWillChangeFrameNotification will be emitted before
|
||||
// UIKeyboardWillHideNotification, so we need to listen to
|
||||
// keyboardWillHide and keyboardWillShow instead of
|
||||
// keyboardWillChangeFrame.
|
||||
Keyboard.addListener('keyboardWillHide', this._onKeyboardHide),
|
||||
Keyboard.addListener('keyboardWillShow', this._onKeyboardChange),
|
||||
];
|
||||
} else {
|
||||
this._subscriptions = [
|
||||
|
||||
@@ -1792,12 +1792,11 @@ 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;
|
||||
_onKeyboardHide: $FlowFixMe;
|
||||
_onLayout: $FlowFixMe;
|
||||
_onDimensionsChange: $FlowFixMe;
|
||||
_setBottom: $FlowFixMe;
|
||||
_updateBottomIfNecessary: $FlowFixMe;
|
||||
componentDidUpdate(_: Props, prevState: State): void;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#import <FBReactNativeSpec/FBReactNativeSpec.h>
|
||||
#import <React/RCTEventDispatcherProtocol.h>
|
||||
#import <React/RCTUtils.h>
|
||||
|
||||
#import "CoreModulesPlugins.h"
|
||||
|
||||
@@ -117,6 +118,10 @@ static NSDictionary *RCTParseKeyboardNotification(NSNotification *notification)
|
||||
static_cast<UIViewAnimationCurve>([userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue]);
|
||||
NSInteger isLocalUserInfoKey = [userInfo[UIKeyboardIsLocalUserInfoKey] integerValue];
|
||||
|
||||
UIWindow *window = RCTKeyWindow();
|
||||
beginFrame = [window convertRect:beginFrame fromCoordinateSpace:window.screen.coordinateSpace];
|
||||
endFrame = [window convertRect:endFrame fromCoordinateSpace:window.screen.coordinateSpace];
|
||||
|
||||
return @{
|
||||
@"startCoordinates" : RCTRectDictionaryValue(beginFrame),
|
||||
@"endCoordinates" : RCTRectDictionaryValue(endFrame),
|
||||
|
||||
Reference in New Issue
Block a user