Skip to content

Commit 427374f

Browse files
authored
fix: wrong useWindowDimensions on iPad (#957)
## 📜 Description Add support for `windowDidResize` event on iOS. ## 💡 Motivation and Context In #948 I changed implementation and added subscription to `screen` values instead of `window`. While it fixes some issues it doesn't work on iPad 🤯 This is very sad that such basic functionality can not be handled by RN out-of-the-box. However we already use own `useWindowDimensions` hook on Android. So in this PR I decided to replicate that behavior on iOS. `react-native-safe-area-context` doesn't have that problem, but `react-native-safe-area-context` handles that event in `layoutSubviews`. After thinking a while I decided to use the same approach and emit event `windowDidResize`. And this approach actually works 🙂 I also added a check to not emit events too frequently if dimensions weren't changed. The PR #956 also aims to solve that problem, but `useWindowDimensions` is used by other components, such as `KeyboardAvoidingView`/`KeyboardAwareScrollView`, so the problem should be fixed in hook itself. Closes #955 ## 📢 Changelog <!-- High level overview of important changes --> <!-- For example: fixed status bar manipulation; added new types declarations; --> <!-- If your changes don't affect one of platform/language below - then remove this platform/language --> ### iOS - emit `windowDidResize` event with device screen dimensions; ## 🤔 How Has This Been Tested? Tested manually in reproduction example + by e2e tests. ## 📸 Screenshots (if appropriate): ![image](https://github.com/user-attachments/assets/d2379f1d-679d-4314-b6d8-840d667c65b6) ## 📝 Checklist - [x] CI successfully passed - [x] I added new mocks and corresponding unit-tests if library API was changed
1 parent c5c0077 commit 427374f

5 files changed

Lines changed: 49 additions & 60 deletions

File tree

ios/KeyboardControllerModule.mm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ - (void)sendEvent:(NSString *)name body:(id)body
119119
@"KeyboardController::keyboardDidHide",
120120
// focused input
121121
@"KeyboardController::focusDidSet",
122+
// window dimensions
123+
@"KeyboardController::windowDidResize",
122124
];
123125
}
124126

ios/views/KeyboardControllerView.mm

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ @interface KeyboardControllerView () <RCTKeyboardControllerViewViewProtocol>
3737
@implementation KeyboardControllerView {
3838
KeyboardMovementObserver *keyboardObserver;
3939
FocusedInputObserver *inputObserver;
40+
41+
CGSize _lastScreenSize;
4042
}
4143

4244
+ (ComponentDescriptorProvider)componentDescriptorProvider
@@ -211,6 +213,22 @@ - (instancetype)initWithFrame:(CGRect)frame
211213
return self;
212214
}
213215

216+
- (void)layoutSubviews
217+
{
218+
[super layoutSubviews];
219+
220+
CGSize screenSize = [UIScreen mainScreen].bounds.size;
221+
if (CGSizeEqualToSize(screenSize, _lastScreenSize)) {
222+
return;
223+
}
224+
225+
_lastScreenSize = screenSize;
226+
227+
NSDictionary *data = @{@"width" : @(screenSize.width), @"height" : @(screenSize.height)};
228+
229+
[KeyboardController.shared sendEvent:@"KeyboardController::windowDidResize" body:data];
230+
}
231+
214232
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
215233
{
216234
const auto &oldViewProps = *std::static_pointer_cast<const KeyboardControllerViewProps>(_props);

ios/views/KeyboardControllerViewManager.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ class KeyboardControllerView: UIView {
1515
private var inputObserver: FocusedInputObserver?
1616
private var eventDispatcher: RCTEventDispatcherProtocol
1717
private var bridge: RCTBridge
18+
// internal state
19+
private var lastScreenSize: CGSize = .zero
1820
// react callbacks
1921
/// keyboard
2022
@objc var onKeyboardMoveStart: RCTDirectEventBlock?
@@ -83,6 +85,23 @@ class KeyboardControllerView: UIView {
8385
}
8486
}
8587

88+
override func layoutSubviews() {
89+
super.layoutSubviews()
90+
91+
let screenSize = UIScreen.main.bounds.size
92+
93+
if lastScreenSize == screenSize {
94+
return
95+
}
96+
97+
lastScreenSize = screenSize
98+
99+
var data = [AnyHashable: Any]()
100+
data["width"] = screenSize.width
101+
data["height"] = screenSize.height
102+
onNotify(event: "KeyboardController::windowDidResize", data: data)
103+
}
104+
86105
func onLayoutChanged(event: NSObject) {
87106
guard isJSThreadReady() else { return }
88107

src/hooks/useWindowDimensions/index.android.ts

Lines changed: 0 additions & 38 deletions
This file was deleted.

src/hooks/useWindowDimensions/index.ts

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { useEffect, useState } from "react";
22
import { Dimensions } from "react-native";
33

4+
import { WindowDimensionsEvents } from "../../bindings";
5+
46
import type { WindowDimensionsEventData } from "../../types";
57

68
const screen = Dimensions.get("screen");
@@ -10,34 +12,20 @@ let initialDimensions: WindowDimensionsEventData = {
1012
height: screen.height,
1113
};
1214

13-
Dimensions.addEventListener("change", (e) => {
14-
initialDimensions = {
15-
width: e.screen.width,
16-
height: e.screen.height,
17-
};
15+
WindowDimensionsEvents.addListener("windowDidResize", (e) => {
16+
initialDimensions = e;
1817
});
1918

20-
/**
21-
* On iOS we need to use `screen`, because this property is derived from `mainScreen.bounds.size`
22-
* while `window` is based on `[RCTKeyWindowValuesProxy sharedInstance].windowSize`, which can have
23-
* out of date values (especially when device gets rotated).
24-
*
25-
* @returns Window dimension.
26-
* @example
27-
* ```
28-
* const { height, window } = useWindowDimensions();
29-
* ```
30-
*/
3119
export const useWindowDimensions = () => {
3220
const [dimensions, setDimensions] = useState(initialDimensions);
3321

3422
useEffect(() => {
35-
const subscription = Dimensions.addEventListener("change", (e) => {
36-
setDimensions({
37-
width: e.screen.width,
38-
height: e.screen.height,
39-
});
40-
});
23+
const subscription = WindowDimensionsEvents.addListener(
24+
"windowDidResize",
25+
(e) => {
26+
setDimensions(e);
27+
},
28+
);
4129

4230
// we might have missed an update between reading a value in render and
4331
// `addListener` in this handler, so we set it here. If there was

0 commit comments

Comments
 (0)