Skip to content

Commit e0aa7a8

Browse files
stareezy-1meta-codesync[bot]
authored andcommitted
Fix keyboard inset state across ScrollView recycling (#57811)
Summary: Closes #57755. `RCTScrollViewComponentView` remained subscribed to keyboard notifications while pooled with `_automaticallyAdjustKeyboardInsets` still enabled. A late notification could therefore restore keyboard insets after `prepareForRecycle` had cleared them. Disarm keyboard inset adjustment during recycling, then always synchronize the flag from the incoming props so a recycled view is correctly re-armed when the next ScrollView opts in. ## Changelog: [IOS] [FIXED] - Prevent recycled ScrollViews from retaining automatic keyboard inset behavior. Pull Request resolved: #57811 Test Plan: - Added `RCTScrollViewComponentViewTests.testAutomaticallyAdjustKeyboardInsetsAcrossRecycling`, covering enabled behavior, ignored keyboard notifications while recycled, and re-enabling after remount. - `/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang-format --dry-run --Werror packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm packages/react-native/React/Tests/Mounting/RCTScrollViewComponentViewTests.mm` - `git diff --check` The full native XCTest suite was not run locally because this sparse checkout does not include a bootstrapped RNTester/CocoaPods workspace. Reviewed By: christophpurrer Differential Revision: D114719614 Pulled By: fabriziocucci fbshipit-source-id: e8e035fe8a74909b2281fc8d52af0e034f2bafe4
1 parent 704c2b1 commit e0aa7a8

2 files changed

Lines changed: 68 additions & 3 deletions

File tree

‎packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -360,9 +360,7 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &
360360
MAP_SCROLL_VIEW_PROP(showsHorizontalScrollIndicator);
361361
MAP_SCROLL_VIEW_PROP(showsVerticalScrollIndicator);
362362

363-
if (oldScrollViewProps.automaticallyAdjustKeyboardInsets != newScrollViewProps.automaticallyAdjustKeyboardInsets) {
364-
_automaticallyAdjustKeyboardInsets = newScrollViewProps.automaticallyAdjustKeyboardInsets;
365-
}
363+
_automaticallyAdjustKeyboardInsets = newScrollViewProps.automaticallyAdjustKeyboardInsets;
366364

367365
if (oldScrollViewProps.scrollIndicatorInsets != newScrollViewProps.scrollIndicatorInsets) {
368366
_scrollView.scrollIndicatorInsets = RCTUIEdgeInsetsFromEdgeInsets(newScrollViewProps.scrollIndicatorInsets);
@@ -702,6 +700,7 @@ - (void)prepareForRecycle
702700
// and keeps it as an opt-in behavior.
703701
_scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
704702
_shouldUpdateContentInsetAdjustmentBehavior = YES;
703+
_automaticallyAdjustKeyboardInsets = NO;
705704
_isUserTriggeredScrolling = NO;
706705
CGRect oldFrame = self.frame;
707706
self.frame = CGRectZero;
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#import <React/RCTScrollViewComponentView.h>
9+
#import <XCTest/XCTest.h>
10+
#import <react/renderer/components/scrollview/ScrollViewProps.h>
11+
#import <react/renderer/components/scrollview/ScrollViewShadowNode.h>
12+
13+
using facebook::react::Props;
14+
using facebook::react::ScrollViewProps;
15+
using facebook::react::ScrollViewShadowNode;
16+
17+
#if TARGET_OS_IOS
18+
19+
static Props::Shared makeScrollViewProps(bool automaticallyAdjustKeyboardInsets)
20+
{
21+
auto props = std::make_shared<ScrollViewProps>();
22+
props->automaticallyAdjustKeyboardInsets = automaticallyAdjustKeyboardInsets;
23+
return props;
24+
}
25+
26+
@interface RCTScrollViewComponentView (Tests)
27+
- (void)_keyboardWillChangeFrame:(NSNotification *)notification;
28+
@end
29+
30+
@interface RCTScrollViewComponentViewTests : XCTestCase
31+
@end
32+
33+
@implementation RCTScrollViewComponentViewTests
34+
35+
- (void)testAutomaticallyAdjustKeyboardInsetsAcrossRecycling
36+
{
37+
RCTScrollViewComponentView *view = [[RCTScrollViewComponentView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
38+
auto props = makeScrollViewProps(true);
39+
[view updateProps:props oldProps:ScrollViewShadowNode::defaultSharedProps()];
40+
41+
NSNotification *notification = [NSNotification
42+
notificationWithName:UIKeyboardWillChangeFrameNotification
43+
object:nil
44+
userInfo:@{
45+
UIKeyboardAnimationDurationUserInfoKey : @0,
46+
UIKeyboardAnimationCurveUserInfoKey : @(UIViewAnimationCurveLinear),
47+
UIKeyboardFrameBeginUserInfoKey : [NSValue valueWithCGRect:CGRectMake(0, 100, 100, 50)],
48+
UIKeyboardFrameEndUserInfoKey : [NSValue valueWithCGRect:CGRectMake(0, 50, 100, 50)],
49+
}];
50+
51+
[view _keyboardWillChangeFrame:notification];
52+
XCTAssertEqual(view.scrollView.contentInset.bottom, 50);
53+
54+
[view prepareForRecycle];
55+
UIEdgeInsets insetsAfterRecycle = view.scrollView.contentInset;
56+
[view _keyboardWillChangeFrame:notification];
57+
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(view.scrollView.contentInset, insetsAfterRecycle));
58+
59+
[view updateProps:props oldProps:nullptr];
60+
[view _keyboardWillChangeFrame:notification];
61+
XCTAssertEqual(view.scrollView.contentInset.bottom, 50);
62+
}
63+
64+
@end
65+
66+
#endif

0 commit comments

Comments
 (0)