From ddbc2613be3cdd4bb6f8c035b287b42a3e2a2b41 Mon Sep 17 00:00:00 2001 From: thomasvo Date: Wed, 11 Mar 2026 12:01:14 -0700 Subject: [PATCH 01/14] fix: handle broken measureInWindow on Fabric + Modal On Fabric, measureInWindow returns modal-surface-relative coordinates instead of screen-absolute coordinates for views inside a Modal (RN bug #52450). This causes automaticOffset to compute incorrect shift values, leaving content hidden behind the keyboard. Detect the bug by comparing measureInWindow results with onLayout coordinates. When they match, estimate absolute Y by assuming the view extends to the bottom of the screen. Also preserve the pre-keyboard frame dimensions when automaticOffset is enabled to prevent iOS modal keyboard adjustment from shrinking the frame mid-animation. Co-Authored-By: Claude Opus 4.6 --- src/components/KeyboardAvoidingView/index.tsx | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/components/KeyboardAvoidingView/index.tsx b/src/components/KeyboardAvoidingView/index.tsx index ae0c78b962..326334b93e 100644 --- a/src/components/KeyboardAvoidingView/index.tsx +++ b/src/components/KeyboardAvoidingView/index.tsx @@ -121,30 +121,49 @@ const KeyboardAvoidingView = forwardRef< if ( keyboard.isClosed.value || initialFrame.value === null || - behavior !== "height" + // When automaticOffset is enabled, always preserve the pre-keyboard + // frame to avoid iOS modal keyboard adjustment shrinking the frame. + // Without automaticOffset, only preserve for "height" behavior + // (existing behavior for backward compatibility). + (!automaticOffset && behavior !== "height") ) { // eslint-disable-next-line react-compiler/react-compiler initialFrame.value = layout; } }, - [behavior], + [behavior, automaticOffset], ); const onLayout = useCallback>( (e) => { const layout = e.nativeEvent.layout; if (automaticOffset) { - // ref is always set here — onLayout only fires after mount - internalRef.current?.measureInWindow((x, y) => { - runOnUI(onLayoutWorklet)({ ...layout, x, y }); - }); + const node = internalRef.current; + if (node) { + node.measureInWindow((x, y) => { + // On Fabric (New Architecture), measureInWindow returns + // modal-surface-relative coordinates for views inside a Modal, + // instead of screen-absolute coordinates (RN bug #52450). + // Detect this by checking if measureInWindow returns the same + // values as onLayout. When detected, estimate the absolute Y + // by assuming the view extends to the bottom of the screen. + const isBrokenMeasurement = + Math.abs(y - layout.y) < 0.5 && + Math.abs(x - layout.x) < 0.5; + const adjustedY = isBrokenMeasurement + ? screenHeight - layout.height + : y; + + runOnUI(onLayoutWorklet)({ ...layout, y: adjustedY }); + }); + } } else { runOnUI(onLayoutWorklet)(layout); } onLayoutProps?.(e); }, - [onLayoutProps, automaticOffset], + [onLayoutProps, automaticOffset, screenHeight], ); const animatedStyle = useAnimatedStyle(() => { From 2df0e98df81add7f50c00e4d5333d97761c90041 Mon Sep 17 00:00:00 2001 From: thomasvo Date: Wed, 11 Mar 2026 13:29:46 -0700 Subject: [PATCH 02/14] fix: use native windowPosition for automaticOffset instead of measureInWindow Replace the heuristic detection of broken measureInWindow with a native windowPosition method that uses UIKit/Android view APIs to get true screen-absolute coordinates. This correctly handles Fabric + Modal where measureInWindow returns surface-relative coordinates (RN bug #52450). Co-Authored-By: Claude Opus 4.6 --- .../KeyboardControllerModule.kt | 5 +++ .../modules/KeyboardControllerModuleImpl.kt | 21 +++++++++++ .../KeyboardControllerModule.kt | 6 ++++ ios/KeyboardControllerModule.mm | 35 +++++++++++++++++++ src/bindings.ts | 1 + src/components/KeyboardAvoidingView/index.tsx | 29 ++++++--------- src/specs/NativeKeyboardController.ts | 1 + src/types/module.ts | 7 ++++ 8 files changed, 87 insertions(+), 18 deletions(-) diff --git a/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt b/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt index 5728c21833..9054e4b2e1 100644 --- a/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +++ b/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt @@ -1,5 +1,6 @@ package com.reactnativekeyboardcontroller +import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.reactnativekeyboardcontroller.modules.KeyboardControllerModuleImpl @@ -33,6 +34,10 @@ class KeyboardControllerModule( module.setFocusTo(direction) } + override fun windowPosition(viewTag: Double, promise: Promise) { + module.windowPosition(viewTag, promise) + } + override fun addListener(eventName: String?) { // Required for RN built-in Event Emitter Calls. } diff --git a/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt b/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt index e2ec849d68..821cbd3411 100644 --- a/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt +++ b/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt @@ -5,6 +5,8 @@ import android.os.Build import android.view.View import android.view.WindowManager import android.view.inputmethod.InputMethodManager +import com.facebook.react.bridge.Arguments +import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.UiThreadUtil import com.reactnativekeyboardcontroller.interactive.KeyboardAnimationController @@ -77,6 +79,25 @@ class KeyboardControllerModuleImpl( } } + fun windowPosition(viewTag: Double, promise: Promise) { + UiThreadUtil.runOnUiThread { + val view = mReactContext.currentActivity?.findViewById(viewTag.toInt()) + if (view == null) { + promise.reject("E_VIEW_NOT_FOUND", "Could not find view for tag") + return@runOnUiThread + } + val location = IntArray(2) + view.getLocationInWindow(location) + val density = mReactContext.resources.displayMetrics.density + val map = Arguments.createMap() + map.putDouble("x", location[0].toDouble() / density) + map.putDouble("y", location[1].toDouble() / density) + map.putDouble("width", view.width.toDouble() / density) + map.putDouble("height", view.height.toDouble() / density) + promise.resolve(map) + } + } + private fun setSoftInputMode(mode: Int) { UiThreadUtil.runOnUiThread { if (getCurrentMode() != mode) { diff --git a/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt b/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt index 6c59c9cf04..34dfa81e94 100644 --- a/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +++ b/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt @@ -1,5 +1,6 @@ package com.reactnativekeyboardcontroller +import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactContextBaseJavaModule import com.facebook.react.bridge.ReactMethod @@ -40,6 +41,11 @@ class KeyboardControllerModule( module.setFocusTo(direction) } + @ReactMethod + fun windowPosition(viewTag: Double, promise: Promise) { + module.windowPosition(viewTag, promise) + } + @Suppress("detekt:UnusedParameter") @ReactMethod fun addListener(eventName: String?) { diff --git a/ios/KeyboardControllerModule.mm b/ios/KeyboardControllerModule.mm index 60ca396b27..65f0f873c1 100644 --- a/ios/KeyboardControllerModule.mm +++ b/ios/KeyboardControllerModule.mm @@ -95,6 +95,41 @@ - (void)setFocusTo:(NSString *)direction [ViewHierarchyNavigator setFocusToDirection:direction]; } +#ifdef RCT_NEW_ARCH_ENABLED +- (void)windowPosition:(double)viewTag + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject +#else +RCT_EXPORT_METHOD(windowPosition : (nonnull NSNumber *)viewTag + resolve : (RCTPromiseResolveBlock)resolve + reject : (RCTPromiseRejectBlock)reject) +#endif +{ + dispatch_async(dispatch_get_main_queue(), ^{ + NSInteger tag; +#ifdef RCT_NEW_ARCH_ENABLED + tag = (NSInteger)viewTag; +#else + tag = viewTag.integerValue; +#endif + UIView *view = [UIApplication.sharedApplication.keyWindow viewWithTag:tag]; + if (!view) { + reject(@"E_VIEW_NOT_FOUND", @"Could not find view for tag", nil); + return; + } + // Use UIKit coordinate conversion to get true window coordinates. + // This bypasses Fabric's shadow tree which returns surface-relative + // coordinates for views inside Modals (RN bug #52450). + CGRect windowFrame = [view.superview convertRect:view.frame toView:nil]; + resolve(@{ + @"x": @(windowFrame.origin.x), + @"y": @(windowFrame.origin.y), + @"width": @(windowFrame.size.width), + @"height": @(windowFrame.size.height), + }); + }); +} + + (KeyboardController *)shared { return shared; diff --git a/src/bindings.ts b/src/bindings.ts index 535ab53d9c..aab44d75c0 100644 --- a/src/bindings.ts +++ b/src/bindings.ts @@ -22,6 +22,7 @@ export const KeyboardControllerNative: KeyboardControllerNativeModule = { preload: NOOP, dismiss: NOOP, setFocusTo: NOOP, + windowPosition: () => Promise.resolve({ x: 0, y: 0, width: 0, height: 0 }), addListener: NOOP, removeListeners: NOOP, }; diff --git a/src/components/KeyboardAvoidingView/index.tsx b/src/components/KeyboardAvoidingView/index.tsx index 326334b93e..043e393e1b 100644 --- a/src/components/KeyboardAvoidingView/index.tsx +++ b/src/components/KeyboardAvoidingView/index.tsx @@ -1,5 +1,5 @@ import React, { forwardRef, useCallback, useMemo } from "react"; -import { View } from "react-native"; +import { findNodeHandle, View } from "react-native"; import Reanimated, { interpolate, runOnUI, @@ -8,6 +8,7 @@ import Reanimated, { useSharedValue, } from "react-native-reanimated"; +import { KeyboardControllerNative } from "../../bindings"; import { useWindowDimensions } from "../../hooks"; import useCombinedRef from "../hooks/useCombinedRef"; @@ -139,22 +140,14 @@ const KeyboardAvoidingView = forwardRef< if (automaticOffset) { const node = internalRef.current; - if (node) { - node.measureInWindow((x, y) => { - // On Fabric (New Architecture), measureInWindow returns - // modal-surface-relative coordinates for views inside a Modal, - // instead of screen-absolute coordinates (RN bug #52450). - // Detect this by checking if measureInWindow returns the same - // values as onLayout. When detected, estimate the absolute Y - // by assuming the view extends to the bottom of the screen. - const isBrokenMeasurement = - Math.abs(y - layout.y) < 0.5 && - Math.abs(x - layout.x) < 0.5; - const adjustedY = isBrokenMeasurement - ? screenHeight - layout.height - : y; - - runOnUI(onLayoutWorklet)({ ...layout, y: adjustedY }); + const tag = node ? findNodeHandle(node) : null; + + if (tag != null) { + // Use native windowPosition to get true screen-absolute coordinates. + // This bypasses Fabric's measureInWindow which returns + // surface-relative coordinates inside Modals (RN bug #52450). + KeyboardControllerNative.windowPosition(tag).then((position) => { + runOnUI(onLayoutWorklet)({ ...layout, y: position.y }); }); } } else { @@ -163,7 +156,7 @@ const KeyboardAvoidingView = forwardRef< onLayoutProps?.(e); }, - [onLayoutProps, automaticOffset, screenHeight], + [onLayoutProps, automaticOffset], ); const animatedStyle = useAnimatedStyle(() => { diff --git a/src/specs/NativeKeyboardController.ts b/src/specs/NativeKeyboardController.ts index aa3a977db8..fa5af61ef7 100644 --- a/src/specs/NativeKeyboardController.ts +++ b/src/specs/NativeKeyboardController.ts @@ -11,6 +11,7 @@ export interface Spec extends TurboModule { preload(): void; dismiss(keepFocus: boolean, animated: boolean): void; setFocusTo(direction: string): void; + windowPosition(viewTag: number): Promise; // event emitter addListener: (eventName: string) => void; diff --git a/src/types/module.ts b/src/types/module.ts index 45fe2d1c85..24af62cfa6 100644 --- a/src/types/module.ts +++ b/src/types/module.ts @@ -109,6 +109,12 @@ export type KeyboardControllerModule = { */ state: () => KeyboardEventData; }; +export type WindowPositionResult = { + x: number; + y: number; + width: number; + height: number; +}; export type KeyboardControllerNativeModule = { // android only setDefaultMode: () => void; @@ -118,6 +124,7 @@ export type KeyboardControllerNativeModule = { // all platforms dismiss: (keepFocus: boolean, animated: boolean) => void; setFocusTo: (direction: Direction) => void; + windowPosition: (viewTag: number) => Promise; // native event module stuff addListener: (eventName: string) => void; removeListeners: (count: number) => void; From a24317a3c414bb653f0d1d1626aa17f10def0da7 Mon Sep 17 00:00:00 2001 From: thomasvo Date: Wed, 11 Mar 2026 13:38:20 -0700 Subject: [PATCH 03/14] fix: pass x coordinate and handle promise rejection in windowPosition - Pass both x and y from windowPosition (matching original measureInWindow behavior) - Add .catch() to handle view-not-found rejection gracefully Co-Authored-By: Claude Opus 4.6 --- src/components/KeyboardAvoidingView/index.tsx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/components/KeyboardAvoidingView/index.tsx b/src/components/KeyboardAvoidingView/index.tsx index 043e393e1b..9d49bd356f 100644 --- a/src/components/KeyboardAvoidingView/index.tsx +++ b/src/components/KeyboardAvoidingView/index.tsx @@ -146,9 +146,18 @@ const KeyboardAvoidingView = forwardRef< // Use native windowPosition to get true screen-absolute coordinates. // This bypasses Fabric's measureInWindow which returns // surface-relative coordinates inside Modals (RN bug #52450). - KeyboardControllerNative.windowPosition(tag).then((position) => { - runOnUI(onLayoutWorklet)({ ...layout, y: position.y }); - }); + KeyboardControllerNative.windowPosition(tag) + .then((position) => { + runOnUI(onLayoutWorklet)({ + ...layout, + x: position.x, + y: position.y, + }); + }) + .catch(() => { + // View may have been unmounted — fall back to onLayout values + runOnUI(onLayoutWorklet)(layout); + }); } } else { runOnUI(onLayoutWorklet)(layout); From daffd8fddae8af3e7db5de9e1c941eaab9b7112a Mon Sep 17 00:00:00 2001 From: thomasvo Date: Wed, 11 Mar 2026 14:02:47 -0700 Subject: [PATCH 04/14] fix: fall back to measureInWindow when windowPosition fails on Paper On Paper architecture, viewWithTag may not find the view, causing windowPosition to reject. Fall back to measureInWindow which returns correct absolute coordinates on Paper. Co-Authored-By: Claude Opus 4.6 --- src/components/KeyboardAvoidingView/index.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/KeyboardAvoidingView/index.tsx b/src/components/KeyboardAvoidingView/index.tsx index 9d49bd356f..a315bd56fd 100644 --- a/src/components/KeyboardAvoidingView/index.tsx +++ b/src/components/KeyboardAvoidingView/index.tsx @@ -155,8 +155,16 @@ const KeyboardAvoidingView = forwardRef< }); }) .catch(() => { - // View may have been unmounted — fall back to onLayout values - runOnUI(onLayoutWorklet)(layout); + // windowPosition failed (e.g. view unmounted or tag not found). + // Fall back to measureInWindow which returns correct absolute + // coordinates on Paper architecture. + if (node) { + node.measureInWindow((x, y) => { + runOnUI(onLayoutWorklet)({ ...layout, x, y }); + }); + } else { + runOnUI(onLayoutWorklet)(layout); + } }); } } else { From c9b85b67724e33c61aa40cdf835f9aa386b35d1a Mon Sep 17 00:00:00 2001 From: thomasvo Date: Wed, 11 Mar 2026 14:04:56 -0700 Subject: [PATCH 05/14] style: fix Kotlin lint function signature formatting Co-Authored-By: Claude Opus 4.6 --- .../KeyboardControllerModule.kt | 5 ++++- .../modules/KeyboardControllerModuleImpl.kt | 5 ++++- .../KeyboardControllerModule.kt | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt b/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt index 9054e4b2e1..3d2b6a613c 100644 --- a/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +++ b/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt @@ -34,7 +34,10 @@ class KeyboardControllerModule( module.setFocusTo(direction) } - override fun windowPosition(viewTag: Double, promise: Promise) { + override fun windowPosition( + viewTag: Double, + promise: Promise, + ) { module.windowPosition(viewTag, promise) } diff --git a/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt b/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt index 821cbd3411..5f286380ec 100644 --- a/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt +++ b/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt @@ -79,7 +79,10 @@ class KeyboardControllerModuleImpl( } } - fun windowPosition(viewTag: Double, promise: Promise) { + fun windowPosition( + viewTag: Double, + promise: Promise, + ) { UiThreadUtil.runOnUiThread { val view = mReactContext.currentActivity?.findViewById(viewTag.toInt()) if (view == null) { diff --git a/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt b/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt index 34dfa81e94..a00e7989c5 100644 --- a/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +++ b/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt @@ -42,7 +42,10 @@ class KeyboardControllerModule( } @ReactMethod - fun windowPosition(viewTag: Double, promise: Promise) { + fun windowPosition( + viewTag: Double, + promise: Promise, + ) { module.windowPosition(viewTag, promise) } From 45d34598a4c3247ebc3539c37085e59d463d0fdd Mon Sep 17 00:00:00 2001 From: thomasvo Date: Wed, 11 Mar 2026 14:11:00 -0700 Subject: [PATCH 06/14] fix: null-tag fallback and keyWindow deprecation - Fall back to onLayout values when findNodeHandle returns null instead of silently dropping the layout event - Replace deprecated keyWindow with UIWindowScene API for iOS 13+ Co-Authored-By: Claude Opus 4.6 --- ios/KeyboardControllerModule.mm | 16 +++++++++++++++- src/components/KeyboardAvoidingView/index.tsx | 2 ++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/ios/KeyboardControllerModule.mm b/ios/KeyboardControllerModule.mm index 65f0f873c1..13b90f343f 100644 --- a/ios/KeyboardControllerModule.mm +++ b/ios/KeyboardControllerModule.mm @@ -112,7 +112,21 @@ - (void)windowPosition:(double)viewTag #else tag = viewTag.integerValue; #endif - UIView *view = [UIApplication.sharedApplication.keyWindow viewWithTag:tag]; + UIWindow *window = nil; + for (UIScene *scene in UIApplication.sharedApplication.connectedScenes) { + if (scene.activationState == UISceneActivationStateForegroundActive && + [scene isKindOfClass:[UIWindowScene class]]) { + UIWindowScene *windowScene = (UIWindowScene *)scene; + for (UIWindow *w in windowScene.windows) { + if (w.isKeyWindow) { + window = w; + break; + } + } + if (window) break; + } + } + UIView *view = [window viewWithTag:tag]; if (!view) { reject(@"E_VIEW_NOT_FOUND", @"Could not find view for tag", nil); return; diff --git a/src/components/KeyboardAvoidingView/index.tsx b/src/components/KeyboardAvoidingView/index.tsx index a315bd56fd..347a885f69 100644 --- a/src/components/KeyboardAvoidingView/index.tsx +++ b/src/components/KeyboardAvoidingView/index.tsx @@ -166,6 +166,8 @@ const KeyboardAvoidingView = forwardRef< runOnUI(onLayoutWorklet)(layout); } }); + } else { + runOnUI(onLayoutWorklet)(layout); } } else { runOnUI(onLayoutWorklet)(layout); From 627c8f6922863aab2969fceef3ca186079849919 Mon Sep 17 00:00:00 2001 From: thomasvo Date: Wed, 11 Mar 2026 14:14:53 -0700 Subject: [PATCH 07/14] style: fix TS lint errors (sort-imports, eqeqeq, ban-types) Co-Authored-By: Claude Opus 4.6 --- src/components/KeyboardAvoidingView/index.tsx | 4 ++-- src/specs/NativeKeyboardController.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/KeyboardAvoidingView/index.tsx b/src/components/KeyboardAvoidingView/index.tsx index 347a885f69..4d2a7425f1 100644 --- a/src/components/KeyboardAvoidingView/index.tsx +++ b/src/components/KeyboardAvoidingView/index.tsx @@ -1,5 +1,5 @@ import React, { forwardRef, useCallback, useMemo } from "react"; -import { findNodeHandle, View } from "react-native"; +import { View, findNodeHandle } from "react-native"; import Reanimated, { interpolate, runOnUI, @@ -142,7 +142,7 @@ const KeyboardAvoidingView = forwardRef< const node = internalRef.current; const tag = node ? findNodeHandle(node) : null; - if (tag != null) { + if (tag !== null) { // Use native windowPosition to get true screen-absolute coordinates. // This bypasses Fabric's measureInWindow which returns // surface-relative coordinates inside Modals (RN bug #52450). diff --git a/src/specs/NativeKeyboardController.ts b/src/specs/NativeKeyboardController.ts index fa5af61ef7..5ccc3ca805 100644 --- a/src/specs/NativeKeyboardController.ts +++ b/src/specs/NativeKeyboardController.ts @@ -11,7 +11,7 @@ export interface Spec extends TurboModule { preload(): void; dismiss(keepFocus: boolean, animated: boolean): void; setFocusTo(direction: string): void; - windowPosition(viewTag: number): Promise; + windowPosition(viewTag: number): Promise; // event emitter addListener: (eventName: string) => void; From 48cb2af0111438b43bbc8b49c497adaebd95be28 Mon Sep 17 00:00:00 2001 From: thomasvo Date: Wed, 11 Mar 2026 15:49:26 -0700 Subject: [PATCH 08/14] style: fix ObjC clang-format Co-Authored-By: Claude Opus 4.6 --- ios/KeyboardControllerModule.mm | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/ios/KeyboardControllerModule.mm b/ios/KeyboardControllerModule.mm index 13b90f343f..f080554106 100644 --- a/ios/KeyboardControllerModule.mm +++ b/ios/KeyboardControllerModule.mm @@ -100,9 +100,10 @@ - (void)windowPosition:(double)viewTag resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject #else -RCT_EXPORT_METHOD(windowPosition : (nonnull NSNumber *)viewTag - resolve : (RCTPromiseResolveBlock)resolve - reject : (RCTPromiseRejectBlock)reject) +RCT_EXPORT_METHOD(windowPosition + : (nonnull NSNumber *)viewTag resolve + : (RCTPromiseResolveBlock)resolve reject + : (RCTPromiseRejectBlock)reject) #endif { dispatch_async(dispatch_get_main_queue(), ^{ @@ -123,7 +124,8 @@ - (void)windowPosition:(double)viewTag break; } } - if (window) break; + if (window) + break; } } UIView *view = [window viewWithTag:tag]; @@ -136,10 +138,10 @@ - (void)windowPosition:(double)viewTag // coordinates for views inside Modals (RN bug #52450). CGRect windowFrame = [view.superview convertRect:view.frame toView:nil]; resolve(@{ - @"x": @(windowFrame.origin.x), - @"y": @(windowFrame.origin.y), - @"width": @(windowFrame.size.width), - @"height": @(windowFrame.size.height), + @"x" : @(windowFrame.origin.x), + @"y" : @(windowFrame.origin.y), + @"width" : @(windowFrame.size.width), + @"height" : @(windowFrame.size.height), }); }); } From 3d54734fed321010cbe1dcf80fe658fa78473c6d Mon Sep 17 00:00:00 2001 From: thomasvo Date: Wed, 11 Mar 2026 15:55:41 -0700 Subject: [PATCH 09/14] fix: use library findNodeHandle wrapper, guard view.superview nil - Import findNodeHandle from library's utils wrapper (consistent with KeyboardAwareScrollView usage) instead of directly from react-native - Add view.superview nil check in iOS windowPosition to avoid silently resolving with CGRectZero when superview is nil Co-Authored-By: Claude Opus 4.6 --- ios/KeyboardControllerModule.mm | 2 +- src/components/KeyboardAvoidingView/index.tsx | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ios/KeyboardControllerModule.mm b/ios/KeyboardControllerModule.mm index f080554106..d7d6c192e0 100644 --- a/ios/KeyboardControllerModule.mm +++ b/ios/KeyboardControllerModule.mm @@ -129,7 +129,7 @@ - (void)windowPosition:(double)viewTag } } UIView *view = [window viewWithTag:tag]; - if (!view) { + if (!view || !view.superview) { reject(@"E_VIEW_NOT_FOUND", @"Could not find view for tag", nil); return; } diff --git a/src/components/KeyboardAvoidingView/index.tsx b/src/components/KeyboardAvoidingView/index.tsx index 4d2a7425f1..ab9246d901 100644 --- a/src/components/KeyboardAvoidingView/index.tsx +++ b/src/components/KeyboardAvoidingView/index.tsx @@ -1,5 +1,5 @@ import React, { forwardRef, useCallback, useMemo } from "react"; -import { View, findNodeHandle } from "react-native"; +import { View } from "react-native"; import Reanimated, { interpolate, runOnUI, @@ -10,6 +10,7 @@ import Reanimated, { import { KeyboardControllerNative } from "../../bindings"; import { useWindowDimensions } from "../../hooks"; +import { findNodeHandle } from "../../utils/findNodeHandle"; import useCombinedRef from "../hooks/useCombinedRef"; import { useKeyboardAnimation, useTranslateAnimation } from "./hooks"; From 404e93679b02e9f56105caeae4db24ec2396234c Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Thu, 12 Mar 2026 13:19:30 +0100 Subject: [PATCH 10/14] fix: Android/Modal measurements --- .../extensions/ReactContext.kt | 12 ++++++++++++ .../modal/ModalAttachedWatcher.kt | 10 ++++------ .../modules/KeyboardControllerModuleImpl.kt | 4 +++- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ReactContext.kt b/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ReactContext.kt index a940750b20..52c966458b 100644 --- a/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ReactContext.kt +++ b/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ReactContext.kt @@ -3,6 +3,18 @@ package com.reactnativekeyboardcontroller.extensions import android.view.View import android.view.ViewGroup import com.facebook.react.bridge.ReactContext +import com.facebook.react.uimanager.UIManagerHelper +import com.facebook.react.uimanager.common.UIManagerType +import com.facebook.react.uimanager.events.EventDispatcher +import com.reactnativekeyboardcontroller.BuildConfig + +private val archType = if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) UIManagerType.FABRIC else UIManagerType.DEFAULT + +val ReactContext.uiManager + get() = UIManagerHelper.getUIManager(this, archType) + +val ReactContext.eventDispatcher: EventDispatcher? + get() = UIManagerHelper.getEventDispatcher(this, archType) val ReactContext.rootView: View? get() = diff --git a/android/src/main/java/com/reactnativekeyboardcontroller/modal/ModalAttachedWatcher.kt b/android/src/main/java/com/reactnativekeyboardcontroller/modal/ModalAttachedWatcher.kt index 32cbf45260..a00689bbf2 100644 --- a/android/src/main/java/com/reactnativekeyboardcontroller/modal/ModalAttachedWatcher.kt +++ b/android/src/main/java/com/reactnativekeyboardcontroller/modal/ModalAttachedWatcher.kt @@ -4,15 +4,14 @@ import android.view.ViewGroup import android.view.WindowManager import androidx.core.view.ViewCompat import com.facebook.react.uimanager.ThemedReactContext -import com.facebook.react.uimanager.UIManagerHelper -import com.facebook.react.uimanager.common.UIManagerType import com.facebook.react.uimanager.events.Event import com.facebook.react.uimanager.events.EventDispatcherListener import com.facebook.react.views.modal.ReactModalHostView import com.facebook.react.views.view.ReactViewGroup -import com.reactnativekeyboardcontroller.BuildConfig import com.reactnativekeyboardcontroller.constants.Keyboard +import com.reactnativekeyboardcontroller.extensions.eventDispatcher import com.reactnativekeyboardcontroller.extensions.removeSelf +import com.reactnativekeyboardcontroller.extensions.uiManager import com.reactnativekeyboardcontroller.listeners.KeyboardAnimationCallback import com.reactnativekeyboardcontroller.listeners.KeyboardAnimationCallbackConfig import com.reactnativekeyboardcontroller.log.Logger @@ -26,9 +25,8 @@ class ModalAttachedWatcher( private val config: KeyboardAnimationCallbackConfig, private var callback: () -> KeyboardAnimationCallback?, ) : EventDispatcherListener { - private val archType = if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) UIManagerType.FABRIC else UIManagerType.DEFAULT - private val uiManager = UIManagerHelper.getUIManager(reactContext.reactApplicationContext, archType) - private val eventDispatcher = UIManagerHelper.getEventDispatcher(reactContext.reactApplicationContext, archType) + private val uiManager = reactContext.uiManager + private val eventDispatcher = reactContext.eventDispatcher override fun onEventDispatch(event: Event<*>) { if (event.eventName != MODAL_SHOW_EVENT) { diff --git a/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt b/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt index 5f286380ec..036c48e716 100644 --- a/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt +++ b/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt @@ -9,6 +9,7 @@ import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.UiThreadUtil +import com.reactnativekeyboardcontroller.extensions.uiManager import com.reactnativekeyboardcontroller.interactive.KeyboardAnimationController import com.reactnativekeyboardcontroller.traversal.FocusedInputHolder import com.reactnativekeyboardcontroller.traversal.ViewHierarchyNavigator @@ -16,6 +17,7 @@ import com.reactnativekeyboardcontroller.traversal.ViewHierarchyNavigator class KeyboardControllerModuleImpl( private val mReactContext: ReactApplicationContext, ) { + private val uiManager = mReactContext.uiManager private val controller = KeyboardAnimationController() private val mDefaultMode: Int = getCurrentMode() @@ -84,7 +86,7 @@ class KeyboardControllerModuleImpl( promise: Promise, ) { UiThreadUtil.runOnUiThread { - val view = mReactContext.currentActivity?.findViewById(viewTag.toInt()) + val view = uiManager?.resolveView(viewTag.toInt()) if (view == null) { promise.reject("E_VIEW_NOT_FOUND", "Could not find view for tag") return@runOnUiThread From 2cc759ecfcc2c06c4d017c32bf3e8c4c089d9543 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Thu, 12 Mar 2026 16:13:36 +0100 Subject: [PATCH 11/14] fix: resolve PR comments --- FabricExample/ios/Podfile.lock | 2 +- .../KeyboardControllerModule.kt | 4 +- .../modules/KeyboardControllerModuleImpl.kt | 16 +++---- .../KeyboardControllerModule.kt | 4 +- ios/KeyboardControllerModule.mm | 43 ++++++------------- ios/extensions/UIApplication.swift | 2 +- jest/index.js | 3 ++ src/bindings.ts | 3 +- src/components/KeyboardAvoidingView/index.tsx | 35 ++++++--------- src/specs/NativeKeyboardController.ts | 2 +- src/types/module.ts | 4 +- 11 files changed, 48 insertions(+), 70 deletions(-) diff --git a/FabricExample/ios/Podfile.lock b/FabricExample/ios/Podfile.lock index ac1dccc996..3c6e916e5a 100644 --- a/FabricExample/ios/Podfile.lock +++ b/FabricExample/ios/Podfile.lock @@ -3184,7 +3184,7 @@ SPEC CHECKSUMS: RNCMaskedView: 63268de1986a098b5f4d1fb5b1bc1e97fade0aee RNGestureHandler: 4f7cc97a71d4fe0fcba38c94acdd969f5f17c91c RNReactNativeHapticFeedback: 63aa39dbd6ef56e9de688210c5761d4007927a7e - RNReanimated: f2cdef8c5ec70e440498b949f9af3ea39f70fcec + RNReanimated: aabfea4a99566babeedbd3e33eb234b9756932bd RNScreens: 74985ca8e102294a60cec7513fa84c936fa0b20b RNWorklets: ab7740c2a152f77ff76a40d52be860d8f128fab1 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 diff --git a/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt b/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt index 3d2b6a613c..811fdb9fd1 100644 --- a/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +++ b/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt @@ -34,11 +34,11 @@ class KeyboardControllerModule( module.setFocusTo(direction) } - override fun windowPosition( + override fun viewPositionInWindow( viewTag: Double, promise: Promise, ) { - module.windowPosition(viewTag, promise) + module.viewPositionInWindow(viewTag, promise) } override fun addListener(eventName: String?) { diff --git a/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt b/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt index 036c48e716..0a5b7262e2 100644 --- a/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt +++ b/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt @@ -9,6 +9,8 @@ import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.UiThreadUtil +import com.reactnativekeyboardcontroller.extensions.dp +import com.reactnativekeyboardcontroller.extensions.screenLocation import com.reactnativekeyboardcontroller.extensions.uiManager import com.reactnativekeyboardcontroller.interactive.KeyboardAnimationController import com.reactnativekeyboardcontroller.traversal.FocusedInputHolder @@ -81,7 +83,7 @@ class KeyboardControllerModuleImpl( } } - fun windowPosition( + fun viewPositionInWindow( viewTag: Double, promise: Promise, ) { @@ -91,14 +93,12 @@ class KeyboardControllerModuleImpl( promise.reject("E_VIEW_NOT_FOUND", "Could not find view for tag") return@runOnUiThread } - val location = IntArray(2) - view.getLocationInWindow(location) - val density = mReactContext.resources.displayMetrics.density + val location = view.screenLocation val map = Arguments.createMap() - map.putDouble("x", location[0].toDouble() / density) - map.putDouble("y", location[1].toDouble() / density) - map.putDouble("width", view.width.toDouble() / density) - map.putDouble("height", view.height.toDouble() / density) + map.putDouble("x", location[0].toFloat().dp) + map.putDouble("y", location[1].toFloat().dp) + map.putDouble("width", view.width.toFloat().dp) + map.putDouble("height", view.height.toFloat().dp) promise.resolve(map) } } diff --git a/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt b/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt index a00e7989c5..f54f59566e 100644 --- a/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +++ b/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt @@ -42,11 +42,11 @@ class KeyboardControllerModule( } @ReactMethod - fun windowPosition( + fun viewPositionInWindow( viewTag: Double, promise: Promise, ) { - module.windowPosition(viewTag, promise) + module.viewPositionInWindow(viewTag, promise) } @Suppress("detekt:UnusedParameter") diff --git a/ios/KeyboardControllerModule.mm b/ios/KeyboardControllerModule.mm index d7d6c192e0..1bdb241b9f 100644 --- a/ios/KeyboardControllerModule.mm +++ b/ios/KeyboardControllerModule.mm @@ -23,6 +23,9 @@ #endif #import +#ifndef RCT_NEW_ARCH_ENABLED +#import +#endif #ifdef RCT_NEW_ARCH_ENABLED @interface KeyboardController () @@ -96,52 +99,34 @@ - (void)setFocusTo:(NSString *)direction } #ifdef RCT_NEW_ARCH_ENABLED -- (void)windowPosition:(double)viewTag +- (void)viewPositionInWindow:(double)viewTag resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject #else -RCT_EXPORT_METHOD(windowPosition +RCT_EXPORT_METHOD(viewPositionInWindow : (nonnull NSNumber *)viewTag resolve : (RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)reject) #endif { dispatch_async(dispatch_get_main_queue(), ^{ - NSInteger tag; + UIView *view = nil; #ifdef RCT_NEW_ARCH_ENABLED - tag = (NSInteger)viewTag; + NSInteger tag = (NSInteger)viewTag; + view = [UIApplication.sharedApplication.activeWindow viewWithTag:tag]; #else - tag = viewTag.integerValue; + view = [self.bridge.uiManager viewForReactTag:viewTag]; #endif - UIWindow *window = nil; - for (UIScene *scene in UIApplication.sharedApplication.connectedScenes) { - if (scene.activationState == UISceneActivationStateForegroundActive && - [scene isKindOfClass:[UIWindowScene class]]) { - UIWindowScene *windowScene = (UIWindowScene *)scene; - for (UIWindow *w in windowScene.windows) { - if (w.isKeyWindow) { - window = w; - break; - } - } - if (window) - break; - } - } - UIView *view = [window viewWithTag:tag]; if (!view || !view.superview) { reject(@"E_VIEW_NOT_FOUND", @"Could not find view for tag", nil); return; } - // Use UIKit coordinate conversion to get true window coordinates. - // This bypasses Fabric's shadow tree which returns surface-relative - // coordinates for views inside Modals (RN bug #52450). - CGRect windowFrame = [view.superview convertRect:view.frame toView:nil]; + CGRect frame = [view.superview convertRect:view.frame toView:nil]; resolve(@{ - @"x" : @(windowFrame.origin.x), - @"y" : @(windowFrame.origin.y), - @"width" : @(windowFrame.size.width), - @"height" : @(windowFrame.size.height), + @"x" : @(frame.origin.x), + @"y" : @(frame.origin.y), + @"width" : @(frame.size.width), + @"height" : @(frame.size.height), }); }); } diff --git a/ios/extensions/UIApplication.swift b/ios/extensions/UIApplication.swift index c226dff53e..4a188c1009 100644 --- a/ios/extensions/UIApplication.swift +++ b/ios/extensions/UIApplication.swift @@ -9,7 +9,7 @@ import Foundation import UIKit public extension UIApplication { - var activeWindow: UIWindow? { + @objc var activeWindow: UIWindow? { if #available(iOS 13.0, *) { for scene in connectedScenes { if scene.activationState == .foregroundActive, diff --git a/jest/index.js b/jest/index.js index f1f79d5228..7ca5a79610 100644 --- a/jest/index.js +++ b/jest/index.js @@ -87,6 +87,9 @@ const mock = { setFocusTo: jest.fn(), isVisible: jest.fn().mockReturnValue(false), state: jest.fn().mockReturnValue(lastKeyboardEvent), + viewPositionInWindow: jest + .fn() + .mockReturnValue(Promise.resolve({ x: 0, y: 0, width: 0, height: 0 })), }, AndroidSoftInputModes: { SOFT_INPUT_ADJUST_NOTHING: 48, diff --git a/src/bindings.ts b/src/bindings.ts index aab44d75c0..65f8c56f2d 100644 --- a/src/bindings.ts +++ b/src/bindings.ts @@ -22,7 +22,8 @@ export const KeyboardControllerNative: KeyboardControllerNativeModule = { preload: NOOP, dismiss: NOOP, setFocusTo: NOOP, - windowPosition: () => Promise.resolve({ x: 0, y: 0, width: 0, height: 0 }), + viewPositionInWindow: () => + Promise.resolve({ x: 0, y: 0, width: 0, height: 0 }), addListener: NOOP, removeListeners: NOOP, }; diff --git a/src/components/KeyboardAvoidingView/index.tsx b/src/components/KeyboardAvoidingView/index.tsx index ab9246d901..b1eb86e7a5 100644 --- a/src/components/KeyboardAvoidingView/index.tsx +++ b/src/components/KeyboardAvoidingView/index.tsx @@ -127,27 +127,29 @@ const KeyboardAvoidingView = forwardRef< // frame to avoid iOS modal keyboard adjustment shrinking the frame. // Without automaticOffset, only preserve for "height" behavior // (existing behavior for backward compatibility). - (!automaticOffset && behavior !== "height") + behavior !== "height" ) { // eslint-disable-next-line react-compiler/react-compiler initialFrame.value = layout; } }, - [behavior, automaticOffset], + [behavior], ); const onLayout = useCallback>( (e) => { + onLayoutProps?.(e); + const layout = e.nativeEvent.layout; if (automaticOffset) { - const node = internalRef.current; - const tag = node ? findNodeHandle(node) : null; + const tag = findNodeHandle(internalRef.current); if (tag !== null) { - // Use native windowPosition to get true screen-absolute coordinates. - // This bypasses Fabric's measureInWindow which returns - // surface-relative coordinates inside Modals (RN bug #52450). - KeyboardControllerNative.windowPosition(tag) + // Use native `viewPositionInWindow` to get true screen-absolute coordinates. + // This fixes current RN bugs: + // - https://github.com/facebook/react-native/pull/56062 + // - https://github.com/facebook/react-native/pull/56056 + return KeyboardControllerNative.viewPositionInWindow(tag) .then((position) => { runOnUI(onLayoutWorklet)({ ...layout, @@ -156,25 +158,12 @@ const KeyboardAvoidingView = forwardRef< }); }) .catch(() => { - // windowPosition failed (e.g. view unmounted or tag not found). - // Fall back to measureInWindow which returns correct absolute - // coordinates on Paper architecture. - if (node) { - node.measureInWindow((x, y) => { - runOnUI(onLayoutWorklet)({ ...layout, x, y }); - }); - } else { - runOnUI(onLayoutWorklet)(layout); - } + runOnUI(onLayoutWorklet)(layout); }); - } else { - runOnUI(onLayoutWorklet)(layout); } - } else { - runOnUI(onLayoutWorklet)(layout); } - onLayoutProps?.(e); + runOnUI(onLayoutWorklet)(layout); }, [onLayoutProps, automaticOffset], ); diff --git a/src/specs/NativeKeyboardController.ts b/src/specs/NativeKeyboardController.ts index 5ccc3ca805..b0acdade2e 100644 --- a/src/specs/NativeKeyboardController.ts +++ b/src/specs/NativeKeyboardController.ts @@ -11,7 +11,7 @@ export interface Spec extends TurboModule { preload(): void; dismiss(keepFocus: boolean, animated: boolean): void; setFocusTo(direction: string): void; - windowPosition(viewTag: number): Promise; + viewPositionInWindow(viewTag: number): Promise; // event emitter addListener: (eventName: string) => void; diff --git a/src/types/module.ts b/src/types/module.ts index 24af62cfa6..62743695cf 100644 --- a/src/types/module.ts +++ b/src/types/module.ts @@ -109,7 +109,7 @@ export type KeyboardControllerModule = { */ state: () => KeyboardEventData; }; -export type WindowPositionResult = { +export type ViewPositionInWindowResult = { x: number; y: number; width: number; @@ -124,7 +124,7 @@ export type KeyboardControllerNativeModule = { // all platforms dismiss: (keepFocus: boolean, animated: boolean) => void; setFocusTo: (direction: Direction) => void; - windowPosition: (viewTag: number) => Promise; + viewPositionInWindow: (viewTag: number) => Promise; // native event module stuff addListener: (eventName: string) => void; removeListeners: (count: number) => void; From 2e6d10df9fc08d2c32e8a50ad1b71efb28f0d6ed Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Thu, 12 Mar 2026 16:20:44 +0100 Subject: [PATCH 12/14] fix: Ts errors --- src/components/KeyboardAvoidingView/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/KeyboardAvoidingView/index.tsx b/src/components/KeyboardAvoidingView/index.tsx index b1eb86e7a5..7b4d2245e6 100644 --- a/src/components/KeyboardAvoidingView/index.tsx +++ b/src/components/KeyboardAvoidingView/index.tsx @@ -163,7 +163,7 @@ const KeyboardAvoidingView = forwardRef< } } - runOnUI(onLayoutWorklet)(layout); + return runOnUI(onLayoutWorklet)(layout); }, [onLayoutProps, automaticOffset], ); From 1316f75ea1b8ac98ed8a8fa212d68b08ddf2b06e Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Thu, 12 Mar 2026 16:40:55 +0100 Subject: [PATCH 13/14] fix: CI --- ios/KeyboardControllerModule.mm | 4 ++-- src/types/module.ts | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ios/KeyboardControllerModule.mm b/ios/KeyboardControllerModule.mm index 1bdb241b9f..41d0a81ca1 100644 --- a/ios/KeyboardControllerModule.mm +++ b/ios/KeyboardControllerModule.mm @@ -100,8 +100,8 @@ - (void)setFocusTo:(NSString *)direction #ifdef RCT_NEW_ARCH_ENABLED - (void)viewPositionInWindow:(double)viewTag - resolve:(RCTPromiseResolveBlock)resolve - reject:(RCTPromiseRejectBlock)reject + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject #else RCT_EXPORT_METHOD(viewPositionInWindow : (nonnull NSNumber *)viewTag resolve diff --git a/src/types/module.ts b/src/types/module.ts index 62743695cf..86117d6b9a 100644 --- a/src/types/module.ts +++ b/src/types/module.ts @@ -124,7 +124,9 @@ export type KeyboardControllerNativeModule = { // all platforms dismiss: (keepFocus: boolean, animated: boolean) => void; setFocusTo: (direction: Direction) => void; - viewPositionInWindow: (viewTag: number) => Promise; + viewPositionInWindow: ( + viewTag: number, + ) => Promise; // native event module stuff addListener: (eventName: string) => void; removeListeners: (count: number) => void; From 88c0babbbb6b3a01653247def6aa4532d2816627 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Thu, 12 Mar 2026 16:45:56 +0100 Subject: [PATCH 14/14] chore: revert undesired changes --- src/components/KeyboardAvoidingView/index.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/components/KeyboardAvoidingView/index.tsx b/src/components/KeyboardAvoidingView/index.tsx index 7b4d2245e6..ec2ee2961c 100644 --- a/src/components/KeyboardAvoidingView/index.tsx +++ b/src/components/KeyboardAvoidingView/index.tsx @@ -123,10 +123,6 @@ const KeyboardAvoidingView = forwardRef< if ( keyboard.isClosed.value || initialFrame.value === null || - // When automaticOffset is enabled, always preserve the pre-keyboard - // frame to avoid iOS modal keyboard adjustment shrinking the frame. - // Without automaticOffset, only preserve for "height" behavior - // (existing behavior for backward compatibility). behavior !== "height" ) { // eslint-disable-next-line react-compiler/react-compiler