@onekeyfe/react-native-pager-view 3.0.157 → 3.0.159
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -4
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerHost.kt +379 -31
- package/android/src/main/java/com/reactnativepagerview/NativeScrollCoordinatorContributions.kt +15 -0
- package/android/src/main/java/com/reactnativepagerview/NativeScrollerPendingRestore.kt +26 -0
- package/android/src/main/java/com/reactnativepagerview/NativeScrollerScrollAwayInsets.kt +41 -0
- package/android/src/main/java/com/reactnativepagerview/NativeScrollerView.kt +600 -0
- package/android/src/main/java/com/reactnativepagerview/NativeScrollerViewManager.kt +145 -0
- package/android/src/main/java/com/reactnativepagerview/PagerViewViewPackage.kt +1 -1
- package/android/src/main/jni/CMakeLists.txt +19 -0
- package/android/src/main/jni/pagerview.h +11 -0
- package/android/src/main/res/values/native_scroll_coordinator_ids.xml +5 -0
- package/android/src/test/java/com/reactnativepagerview/NativeScrollCoordinatorContributionsTest.kt +26 -0
- package/android/src/test/java/com/reactnativepagerview/NativeScrollerPendingRestoreTest.kt +31 -0
- package/android/src/test/java/com/reactnativepagerview/NativeScrollerScrollAwayInsetsTest.kt +75 -0
- package/common/cpp/react/renderer/components/pagerview/RNCNativeScrollerComponentDescriptor.h +8 -0
- package/common/cpp/react/renderer/components/pagerview/RNCNativeScrollerShadowNode.cpp +15 -0
- package/common/cpp/react/renderer/components/pagerview/RNCNativeScrollerShadowNode.h +23 -0
- package/common/cpp/react/renderer/components/pagerview/RNCNativeScrollerState.h +33 -0
- package/docs/SPEC.md +451 -0
- package/ios/RNCCollapsiblePagerReleasedStatePolicy.h +8 -0
- package/ios/RNCCollapsiblePagerReleasedStatePolicy.m +13 -0
- package/ios/RNCCollapsiblePagerViewComponentView.mm +420 -2
- package/ios/RNCNativeScrollerComponentView.h +17 -0
- package/ios/RNCNativeScrollerComponentView.mm +479 -0
- package/ios/tests/RNCCollapsiblePagerHitTestTests.mm +80 -0
- package/ios/tests/RNCCollapsiblePagerPressCancellationTests.mm +788 -0
- package/ios/tests/RNCCollapsiblePagerPressCancellationWiringTests.mm +109 -0
- package/ios/tests/RNCCollapsiblePagerRefreshControlTests.mm +214 -0
- package/ios/tests/RNCCollapsiblePagerReleasedStatePolicyTests.mm +23 -0
- package/ios/tests/RNCNativeScrollerTests.mm +252 -0
- package/lib/module/CollapsiblePagerView.js +10 -1
- package/lib/module/NativeScroller.js +171 -11
- package/lib/module/NativeScrollerNativeComponent.ts +101 -0
- package/lib/typescript/src/CollapsiblePagerView.d.ts +3 -3
- package/lib/typescript/src/NativeScroller.d.ts +15 -7
- package/lib/typescript/src/NativeScrollerNativeComponent.d.ts +79 -0
- package/package.json +8 -4
- package/react-native-pager-view.podspec +2 -1
- package/react-native.config.js +15 -0
- package/src/CollapsiblePagerView.tsx +14 -4
- package/src/NativeScroller.tsx +291 -14
- package/src/NativeScrollerNativeComponent.ts +101 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#import <XCTest/XCTest.h>
|
|
2
|
+
#import <React/RCTSurfaceTouchHandler.h>
|
|
3
|
+
|
|
4
|
+
#import "RNCCollapsiblePagerViewComponentView.h"
|
|
5
|
+
#import <react/renderer/components/pagerview/Props.h>
|
|
6
|
+
|
|
7
|
+
using namespace facebook::react;
|
|
8
|
+
|
|
9
|
+
@interface RNCPressWiringTestTouch : UITouch
|
|
10
|
+
@property (nonatomic, strong) UIView *testView;
|
|
11
|
+
@end
|
|
12
|
+
@implementation RNCPressWiringTestTouch
|
|
13
|
+
- (UIView *)view { return self.testView; }
|
|
14
|
+
@end
|
|
15
|
+
|
|
16
|
+
@interface RNCCollapsiblePagerPressCancellationWiringTests : XCTestCase
|
|
17
|
+
@property (nonatomic, strong) UIWindow *window;
|
|
18
|
+
@property (nonatomic, strong) UIView *host;
|
|
19
|
+
@property (nonatomic, strong) UIView *surface;
|
|
20
|
+
@property (nonatomic, strong) RCTSurfaceTouchHandler *handler;
|
|
21
|
+
@property (nonatomic, strong) RNCCollapsiblePagerViewComponentView *pager;
|
|
22
|
+
@end
|
|
23
|
+
|
|
24
|
+
@implementation RNCCollapsiblePagerPressCancellationWiringTests
|
|
25
|
+
|
|
26
|
+
- (void)setUp
|
|
27
|
+
{
|
|
28
|
+
[super setUp];
|
|
29
|
+
// Window membership installs the bridge without presenting or taking focus.
|
|
30
|
+
self.window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 320, 600)];
|
|
31
|
+
self.host = [[UIView alloc] initWithFrame:self.window.bounds];
|
|
32
|
+
self.surface = [[UIView alloc] initWithFrame:self.host.bounds];
|
|
33
|
+
[self.window addSubview:self.host];
|
|
34
|
+
[self.host addSubview:self.surface];
|
|
35
|
+
self.handler = [RCTSurfaceTouchHandler new];
|
|
36
|
+
[self.handler attachToView:self.surface];
|
|
37
|
+
self.pager = [[RNCCollapsiblePagerViewComponentView alloc] init];
|
|
38
|
+
auto props = std::make_shared<RNCCollapsiblePagerViewProps>();
|
|
39
|
+
props->nativeSmoothHeaderScrollEnabled = true;
|
|
40
|
+
[self.pager updateProps:props oldProps:nullptr];
|
|
41
|
+
self.pager.frame = self.surface.bounds;
|
|
42
|
+
[self.surface addSubview:self.pager];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
- (void)tearDown
|
|
46
|
+
{
|
|
47
|
+
[self.pager removeFromSuperview];
|
|
48
|
+
[self.handler detachFromView:self.surface];
|
|
49
|
+
self.pager = nil;
|
|
50
|
+
self.handler = nil;
|
|
51
|
+
self.surface = nil;
|
|
52
|
+
self.host = nil;
|
|
53
|
+
self.window = nil;
|
|
54
|
+
[super tearDown];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
- (void)testInstalledBridgeCancelsTouchesAndFiltersItsContentScope
|
|
58
|
+
{
|
|
59
|
+
UIGestureRecognizer *bridge = [self.pager valueForKey:@"contentPressCancellationGesture"];
|
|
60
|
+
XCTAssertNotNil(bridge);
|
|
61
|
+
XCTAssertTrue([bridge isKindOfClass:NSClassFromString(@"RNCCollapsiblePagerContentPressCancellationGestureRecognizer")]);
|
|
62
|
+
XCTAssertEqual(bridge.view, self.host);
|
|
63
|
+
XCTAssertEqual([bridge valueForKey:@"reactTouchHandler"], self.handler);
|
|
64
|
+
XCTAssertTrue(bridge.cancelsTouchesInView);
|
|
65
|
+
XCTAssertFalse(bridge.delaysTouchesBegan);
|
|
66
|
+
XCTAssertFalse(bridge.delaysTouchesEnded);
|
|
67
|
+
XCTAssertTrue([self.handler canBePreventedByGestureRecognizer:bridge]);
|
|
68
|
+
|
|
69
|
+
UIScrollView *scrollView = [self.pager valueForKey:@"pagerScrollView"];
|
|
70
|
+
XCTAssertNotNil(scrollView);
|
|
71
|
+
UIView *content = [UIView new];
|
|
72
|
+
[scrollView addSubview:content];
|
|
73
|
+
UIView *header = [self.pager valueForKey:@"sharedHeaderHostView"];
|
|
74
|
+
// Smooth mode reparents the shared header into the same content subtree.
|
|
75
|
+
[scrollView addSubview:header];
|
|
76
|
+
UIView *headerButton = [UIView new];
|
|
77
|
+
[header addSubview:headerButton];
|
|
78
|
+
RNCPressWiringTestTouch *touch = [RNCPressWiringTestTouch new];
|
|
79
|
+
id<UIGestureRecognizerDelegate> delegate = bridge.delegate;
|
|
80
|
+
XCTAssertEqual((id)delegate, self.pager);
|
|
81
|
+
touch.testView = content;
|
|
82
|
+
XCTAssertTrue([delegate gestureRecognizer:bridge shouldReceiveTouch:touch]);
|
|
83
|
+
touch.testView = headerButton;
|
|
84
|
+
XCTAssertFalse([delegate gestureRecognizer:bridge shouldReceiveTouch:touch]);
|
|
85
|
+
touch.testView = self.surface;
|
|
86
|
+
XCTAssertFalse([delegate gestureRecognizer:bridge shouldReceiveTouch:touch]);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
- (void)testWindowDetachUnbindsInstalledBridge
|
|
90
|
+
{
|
|
91
|
+
UIGestureRecognizer *bridge = [self.pager valueForKey:@"contentPressCancellationGesture"];
|
|
92
|
+
XCTAssertNotNil(bridge);
|
|
93
|
+
[self.pager removeFromSuperview];
|
|
94
|
+
XCTAssertNil(bridge.view);
|
|
95
|
+
XCTAssertNil([bridge valueForKey:@"reactTouchHandler"]);
|
|
96
|
+
XCTAssertNil([self.pager valueForKey:@"contentPressCancellationGesture"]);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
- (void)testRecycleUnbindsInstalledBridge
|
|
100
|
+
{
|
|
101
|
+
UIGestureRecognizer *bridge = [self.pager valueForKey:@"contentPressCancellationGesture"];
|
|
102
|
+
XCTAssertNotNil(bridge);
|
|
103
|
+
[self.pager prepareForRecycle];
|
|
104
|
+
XCTAssertNil(bridge.view);
|
|
105
|
+
XCTAssertNil([bridge valueForKey:@"reactTouchHandler"]);
|
|
106
|
+
XCTAssertNil([self.pager valueForKey:@"contentPressCancellationGesture"]);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@end
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
#import <XCTest/XCTest.h>
|
|
2
|
+
#import "RNCCollapsiblePagerViewComponentView.h"
|
|
3
|
+
|
|
4
|
+
@interface RNCCollapsiblePagerViewComponentView (RefreshControlTesting)
|
|
5
|
+
- (void)updateRefreshControlForScrollView:(UIScrollView *)scrollView;
|
|
6
|
+
- (void)releaseRefreshControlForScrollView:(UIScrollView *)scrollView;
|
|
7
|
+
- (void)attachScrollObserverForCurrentPage;
|
|
8
|
+
@end
|
|
9
|
+
|
|
10
|
+
@interface RNCCollapsiblePagerRefreshControlTests : XCTestCase
|
|
11
|
+
@end
|
|
12
|
+
|
|
13
|
+
@implementation RNCCollapsiblePagerRefreshControlTests
|
|
14
|
+
|
|
15
|
+
- (RNCCollapsiblePagerViewComponentView *)pagerWithHeader:(CGFloat)height
|
|
16
|
+
{
|
|
17
|
+
RNCCollapsiblePagerViewComponentView *pager = [RNCCollapsiblePagerViewComponentView new];
|
|
18
|
+
[pager setValue:@YES forKey:@"nativeSmoothHeaderScrollEnabled"];
|
|
19
|
+
[pager setValue:@(height) forKey:@"headerHeight"];
|
|
20
|
+
[pager setValue:@44 forKey:@"stickyHeaderHeight"];
|
|
21
|
+
return pager;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
- (void)testLateControlAndCallerOffsetUpdatesPreserveBaseline
|
|
25
|
+
{
|
|
26
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithHeader:64];
|
|
27
|
+
UIScrollView *scroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 600)];
|
|
28
|
+
[pager updateRefreshControlForScrollView:scroll];
|
|
29
|
+
UIRefreshControl *control = [UIRefreshControl new];
|
|
30
|
+
control.bounds = CGRectMake(3, -12, 320, 60);
|
|
31
|
+
scroll.refreshControl = control;
|
|
32
|
+
XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, -108, 0.001);
|
|
33
|
+
XCTAssertEqualWithAccuracy(control.bounds.origin.y, -12, 0.001);
|
|
34
|
+
[pager updateRefreshControlForScrollView:scroll];
|
|
35
|
+
XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, -108, 0.001);
|
|
36
|
+
XCTAssertEqualWithAccuracy(control.bounds.origin.y, -12, 0.001);
|
|
37
|
+
control.layer.sublayerTransform = control.layer.sublayerTransform;
|
|
38
|
+
// Also cover an explicit no-op KVO notification, even if CALayer's setter
|
|
39
|
+
// suppresses notifications for equal transforms on the current OS.
|
|
40
|
+
[control.layer willChangeValueForKey:@"sublayerTransform"];
|
|
41
|
+
[control.layer didChangeValueForKey:@"sublayerTransform"];
|
|
42
|
+
XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, -108, 0.001);
|
|
43
|
+
|
|
44
|
+
// Fabric's progressViewOffset setter replaces bounds.origin.y directly.
|
|
45
|
+
control.bounds = CGRectMake(3, -20, 320, 60);
|
|
46
|
+
XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, -108, 0.001);
|
|
47
|
+
XCTAssertEqualWithAccuracy(control.bounds.origin.y, -20, 0.001);
|
|
48
|
+
[pager setValue:@100 forKey:@"headerHeight"];
|
|
49
|
+
[pager updateRefreshControlForScrollView:scroll];
|
|
50
|
+
XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, -144, 0.001);
|
|
51
|
+
XCTAssertEqualWithAccuracy(control.bounds.origin.y, -20, 0.001);
|
|
52
|
+
[pager releaseRefreshControlForScrollView:scroll];
|
|
53
|
+
XCTAssertEqualWithAccuracy(control.bounds.origin.y, -20, 0.001);
|
|
54
|
+
XCTAssertEqualWithAccuracy(control.bounds.origin.x, 3, 0.001);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
- (void)testUIKitSizeChangesAndRefreshCycleDoNotAccumulateOffset
|
|
58
|
+
{
|
|
59
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithHeader:64];
|
|
60
|
+
UIScrollView *scroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 600)];
|
|
61
|
+
// UIKit ignores offscreen beginRefreshing calls. Exercise the actual refresh
|
|
62
|
+
// lifecycle in a visible window rather than weakening the state assertion.
|
|
63
|
+
UIWindow *window = [[UIWindow alloc] initWithFrame:scroll.frame];
|
|
64
|
+
UIViewController *controller = [UIViewController new];
|
|
65
|
+
window.rootViewController = controller;
|
|
66
|
+
scroll.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
|
67
|
+
[controller.view addSubview:scroll];
|
|
68
|
+
window.hidden = NO;
|
|
69
|
+
[self addTeardownBlock:^{ window.hidden = YES; }];
|
|
70
|
+
UIRefreshControl *control = [UIRefreshControl new];
|
|
71
|
+
scroll.refreshControl = control;
|
|
72
|
+
[window layoutIfNeeded];
|
|
73
|
+
[scroll layoutIfNeeded];
|
|
74
|
+
XCTAssertEqual(control.window, window);
|
|
75
|
+
[pager updateRefreshControlForScrollView:scroll];
|
|
76
|
+
CGRect bounds = control.bounds;
|
|
77
|
+
bounds.size = CGSizeMake(320, 60);
|
|
78
|
+
control.bounds = bounds;
|
|
79
|
+
XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, -108, 0.001);
|
|
80
|
+
XCTAssertEqualWithAccuracy(control.bounds.origin.y, 0, 0.001);
|
|
81
|
+
[control beginRefreshing];
|
|
82
|
+
[control layoutIfNeeded];
|
|
83
|
+
[pager updateRefreshControlForScrollView:scroll];
|
|
84
|
+
XCTAssertTrue(control.isRefreshing);
|
|
85
|
+
XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, -108, 0.001);
|
|
86
|
+
XCTAssertEqualWithAccuracy(control.bounds.origin.y, 0, 0.001);
|
|
87
|
+
[control endRefreshing];
|
|
88
|
+
[control layoutIfNeeded];
|
|
89
|
+
XCTAssertFalse(control.isRefreshing);
|
|
90
|
+
XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, -108, 0.001);
|
|
91
|
+
XCTAssertEqualWithAccuracy(control.bounds.origin.y, 0, 0.001);
|
|
92
|
+
[pager releaseRefreshControlForScrollView:scroll];
|
|
93
|
+
XCTAssertEqualWithAccuracy(control.bounds.origin.y, 0, 0.001);
|
|
94
|
+
XCTAssertTrue(CATransform3DIsIdentity(control.layer.sublayerTransform));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
- (void)testReplacementAndNestedOwnerReleaseRestoreOnlyTheirContributions
|
|
98
|
+
{
|
|
99
|
+
RNCCollapsiblePagerViewComponentView *first = [self pagerWithHeader:64];
|
|
100
|
+
RNCCollapsiblePagerViewComponentView *second = [self pagerWithHeader:20];
|
|
101
|
+
UICollectionView *scroll = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, 320, 600)
|
|
102
|
+
collectionViewLayout:[UICollectionViewFlowLayout new]];
|
|
103
|
+
UIRefreshControl *original = [UIRefreshControl new];
|
|
104
|
+
scroll.refreshControl = original;
|
|
105
|
+
[first updateRefreshControlForScrollView:scroll];
|
|
106
|
+
[second updateRefreshControlForScrollView:scroll];
|
|
107
|
+
XCTAssertEqualWithAccuracy(original.layer.sublayerTransform.m42, -172, 0.001);
|
|
108
|
+
[first releaseRefreshControlForScrollView:scroll];
|
|
109
|
+
XCTAssertEqualWithAccuracy(original.layer.sublayerTransform.m42, -64, 0.001);
|
|
110
|
+
|
|
111
|
+
UIRefreshControl *replacement = [UIRefreshControl new];
|
|
112
|
+
replacement.bounds = CGRectMake(0, -8, 320, 60);
|
|
113
|
+
scroll.refreshControl = replacement;
|
|
114
|
+
XCTAssertTrue(CATransform3DIsIdentity(original.layer.sublayerTransform));
|
|
115
|
+
XCTAssertEqualWithAccuracy(replacement.layer.sublayerTransform.m42, -64, 0.001);
|
|
116
|
+
XCTAssertEqualWithAccuracy(replacement.bounds.origin.y, -8, 0.001);
|
|
117
|
+
[second prepareForRecycle];
|
|
118
|
+
XCTAssertEqualWithAccuracy(replacement.bounds.origin.y, -8, 0.001);
|
|
119
|
+
XCTAssertTrue(CATransform3DIsIdentity(replacement.layer.sublayerTransform));
|
|
120
|
+
scroll.refreshControl = nil;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
- (void)testPagerDestructionRestoresControl
|
|
124
|
+
{
|
|
125
|
+
UIScrollView *scroll = [UIScrollView new];
|
|
126
|
+
UIRefreshControl *control = [UIRefreshControl new];
|
|
127
|
+
scroll.refreshControl = control;
|
|
128
|
+
@autoreleasepool {
|
|
129
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithHeader:64];
|
|
130
|
+
[pager updateRefreshControlForScrollView:scroll];
|
|
131
|
+
XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, -108, 0.001);
|
|
132
|
+
XCTAssertEqualWithAccuracy(control.bounds.origin.y, 0, 0.001);
|
|
133
|
+
}
|
|
134
|
+
XCTAssertEqualWithAccuracy(control.bounds.origin.y, 0, 0.001);
|
|
135
|
+
XCTAssertTrue(CATransform3DIsIdentity(control.layer.sublayerTransform));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
- (void)testCallerOffsetCollisionDoesNotAffectVisualContribution
|
|
139
|
+
{
|
|
140
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithHeader:56];
|
|
141
|
+
UIScrollView *scroll = [UIScrollView new];
|
|
142
|
+
UIRefreshControl *control = [UIRefreshControl new];
|
|
143
|
+
scroll.refreshControl = control;
|
|
144
|
+
control.bounds = CGRectMake(0, -200, 320, 60);
|
|
145
|
+
[pager updateRefreshControlForScrollView:scroll];
|
|
146
|
+
control.bounds = CGRectMake(0, -100, 320, 60);
|
|
147
|
+
XCTAssertEqualWithAccuracy(control.bounds.origin.y, -100, 0.001);
|
|
148
|
+
XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, -100, 0.001);
|
|
149
|
+
[pager releaseRefreshControlForScrollView:scroll];
|
|
150
|
+
XCTAssertEqualWithAccuracy(control.bounds.origin.y, -100, 0.001);
|
|
151
|
+
XCTAssertTrue(CATransform3DIsIdentity(control.layer.sublayerTransform));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
- (void)testNestedOwnerDestructionPreservesBaselineAndRestoresClipping
|
|
155
|
+
{
|
|
156
|
+
RNCCollapsiblePagerViewComponentView *second = [self pagerWithHeader:20];
|
|
157
|
+
UIScrollView *scroll = [UIScrollView new];
|
|
158
|
+
UIRefreshControl *control = [UIRefreshControl new];
|
|
159
|
+
scroll.refreshControl = control;
|
|
160
|
+
CATransform3D baseline = CATransform3DMakeTranslation(7, 13, 0);
|
|
161
|
+
control.layer.sublayerTransform = baseline;
|
|
162
|
+
control.layer.masksToBounds = YES;
|
|
163
|
+
@autoreleasepool {
|
|
164
|
+
RNCCollapsiblePagerViewComponentView *first = [self pagerWithHeader:64];
|
|
165
|
+
[first updateRefreshControlForScrollView:scroll];
|
|
166
|
+
[second updateRefreshControlForScrollView:scroll];
|
|
167
|
+
XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, 13 - 172, 0.001);
|
|
168
|
+
XCTAssertFalse(control.layer.masksToBounds);
|
|
169
|
+
}
|
|
170
|
+
XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, 13 - 64, 0.001);
|
|
171
|
+
// A UIKit layer reset must not discard the remaining visual contribution.
|
|
172
|
+
control.layer.sublayerTransform = baseline;
|
|
173
|
+
control.layer.masksToBounds = YES;
|
|
174
|
+
XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, 13 - 64, 0.001);
|
|
175
|
+
XCTAssertFalse(control.layer.masksToBounds);
|
|
176
|
+
[second releaseRefreshControlForScrollView:scroll];
|
|
177
|
+
XCTAssertTrue(CATransform3DEqualToTransform(control.layer.sublayerTransform, baseline));
|
|
178
|
+
XCTAssertTrue(control.layer.masksToBounds);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
- (void)testRefreshTopInsetDoesNotDisplaceAttachedSharedHeader
|
|
182
|
+
{
|
|
183
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithHeader:64];
|
|
184
|
+
UIScrollView *scroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 600)];
|
|
185
|
+
scroll.refreshControl = [UIRefreshControl new];
|
|
186
|
+
scroll.contentSize = CGSizeMake(320, 2000);
|
|
187
|
+
scroll.contentInset = UIEdgeInsetsMake(232, 0, 0, 0);
|
|
188
|
+
UIViewController *page = [UIViewController new];
|
|
189
|
+
[page.view addSubview:scroll];
|
|
190
|
+
NSMutableArray<UIViewController *> *pages = [pager valueForKey:@"pageControllers"];
|
|
191
|
+
[pages addObject:page];
|
|
192
|
+
[pager attachScrollObserverForCurrentPage];
|
|
193
|
+
UIView *headerHost = [pager valueForKey:@"sharedHeaderHostView"];
|
|
194
|
+
XCTAssertEqual(headerHost.superview, scroll);
|
|
195
|
+
XCTAssertEqualWithAccuracy(scroll.contentInset.top, 340, 0.001);
|
|
196
|
+
XCTAssertEqualWithAccuracy(headerHost.transform.ty, 0, 0.001);
|
|
197
|
+
|
|
198
|
+
scroll.contentOffset = CGPointMake(0, 500);
|
|
199
|
+
CGPoint offset = scroll.contentOffset;
|
|
200
|
+
scroll.contentInset = UIEdgeInsetsMake(400, 0, 0, 0);
|
|
201
|
+
XCTAssertTrue(CGPointEqualToPoint(scroll.contentOffset, offset));
|
|
202
|
+
XCTAssertEqualWithAccuracy(headerHost.transform.ty, -60, 0.001);
|
|
203
|
+
|
|
204
|
+
scroll.contentInset = UIEdgeInsetsMake(340, 0, 0, 0);
|
|
205
|
+
XCTAssertEqualWithAccuracy(headerHost.transform.ty, 0, 0.001);
|
|
206
|
+
|
|
207
|
+
scroll.contentInset = UIEdgeInsetsMake(400, 0, 0, 0);
|
|
208
|
+
XCTAssertEqualWithAccuracy(headerHost.transform.ty, -60, 0.001);
|
|
209
|
+
scroll.refreshControl = nil;
|
|
210
|
+
XCTAssertEqualWithAccuracy(headerHost.transform.ty, 0, 0.001);
|
|
211
|
+
[pager prepareForRecycle];
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
@end
|
|
@@ -7,6 +7,29 @@
|
|
|
7
7
|
|
|
8
8
|
@implementation RNCCollapsiblePagerReleasedStatePolicyTests
|
|
9
9
|
|
|
10
|
+
- (void)testRestorationAfterInactiveContentShrinkCannotReapplyStaleCache
|
|
11
|
+
{
|
|
12
|
+
// The list already corrected to -44 while inactive, but the pager still
|
|
13
|
+
// retains the old logical offset captured before rows were removed.
|
|
14
|
+
CGFloat savedLogicalOffset = 513.67;
|
|
15
|
+
CGFloat restored = RNCLimitRestoredScrollOffset(
|
|
16
|
+
savedLogicalOffset - 108, 132, 708, 108, 532);
|
|
17
|
+
XCTAssertEqualWithAccuracy(restored, -44, 0.001);
|
|
18
|
+
XCTAssertEqualWithAccuracy(restored + 108, 64, 0.001);
|
|
19
|
+
XCTAssertEqualWithAccuracy(
|
|
20
|
+
RNCLimitRestoredScrollOffset(restored, 132, 708, 108, 532), restored, 0.001);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
- (void)testRestorationPreservesValidDeepAndNegativeOffsets
|
|
24
|
+
{
|
|
25
|
+
XCTAssertEqualWithAccuracy(
|
|
26
|
+
RNCLimitRestoredScrollOffset(600, 2400, 708, 108, 0), 600, 0.001);
|
|
27
|
+
XCTAssertEqualWithAccuracy(
|
|
28
|
+
RNCLimitRestoredScrollOffset(-160, 132, 708, 108, 532), -160, 0.001);
|
|
29
|
+
XCTAssertEqualWithAccuracy(
|
|
30
|
+
RNCLimitRestoredScrollOffset(600, 0, 0, 108, 0), 600, 0.001);
|
|
31
|
+
}
|
|
32
|
+
|
|
10
33
|
- (void)testPrunesRemovedKeysAndKeepsStableKeysAcrossReorders
|
|
11
34
|
{
|
|
12
35
|
NSObject *alpha = [NSObject new];
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
#import <XCTest/XCTest.h>
|
|
2
|
+
#import "RNCNativeScrollerComponentView.h"
|
|
3
|
+
#import "RNCCollapsiblePagerViewComponentView.h"
|
|
4
|
+
#import <react/renderer/components/pagerview/Props.h>
|
|
5
|
+
|
|
6
|
+
using namespace facebook::react;
|
|
7
|
+
|
|
8
|
+
@interface RNCNativeScrollerComponentView (Tests)
|
|
9
|
+
- (void)emitScrollEvent:(NSInteger)kind velocity:(CGPoint)velocity target:(CGPoint)target;
|
|
10
|
+
- (void)clampContentOffsetIfNeeded;
|
|
11
|
+
- (void)scrollTo:(double)x y:(double)y animated:(BOOL)animated;
|
|
12
|
+
- (void)setRefreshing:(BOOL)refreshing;
|
|
13
|
+
- (CGFloat)contentViewportHeight;
|
|
14
|
+
@end
|
|
15
|
+
|
|
16
|
+
@interface RNCCollapsiblePagerViewComponentView (ScrollerTests)
|
|
17
|
+
- (void)applyInsetsToScrollView:(UIScrollView *)scrollView pageIndex:(NSInteger)index restore:(BOOL)restore;
|
|
18
|
+
- (void)restoreScrollViewState:(UIScrollView *)scrollView resetSavedOffset:(BOOL)reset;
|
|
19
|
+
@end
|
|
20
|
+
|
|
21
|
+
@interface RNCObservedScroller : RNCNativeScrollerComponentView
|
|
22
|
+
@property (nonatomic, strong) NSMutableArray<NSNumber *> *events;
|
|
23
|
+
@end
|
|
24
|
+
@implementation RNCObservedScroller
|
|
25
|
+
- (void)emitScrollEvent:(NSInteger)kind velocity:(CGPoint)velocity target:(CGPoint)target
|
|
26
|
+
{
|
|
27
|
+
if (self.events == nil) self.events = [NSMutableArray new];
|
|
28
|
+
[self.events addObject:@(kind)];
|
|
29
|
+
[super emitScrollEvent:kind velocity:velocity target:target];
|
|
30
|
+
}
|
|
31
|
+
@end
|
|
32
|
+
|
|
33
|
+
@interface RNCNativeScrollerTests : XCTestCase
|
|
34
|
+
@property (nonatomic, strong) RNCObservedScroller *scroller;
|
|
35
|
+
@end
|
|
36
|
+
@implementation RNCNativeScrollerTests
|
|
37
|
+
|
|
38
|
+
- (void)setUp
|
|
39
|
+
{
|
|
40
|
+
[super setUp];
|
|
41
|
+
self.scroller = [RNCObservedScroller new];
|
|
42
|
+
LayoutMetrics metrics;
|
|
43
|
+
metrics.frame.size = {320, 600};
|
|
44
|
+
[self.scroller updateLayoutMetrics:metrics oldLayoutMetrics:{}];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
- (void)tearDown
|
|
48
|
+
{
|
|
49
|
+
[self.scroller prepareForRecycle];
|
|
50
|
+
self.scroller = nil;
|
|
51
|
+
[super tearDown];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
- (void)testContentSizeDoesNotRelayoutReactContentOrSharedHeader
|
|
55
|
+
{
|
|
56
|
+
RCTViewComponentView *content = [RCTViewComponentView new];
|
|
57
|
+
content.frame = CGRectMake(0, 0, 320, 900);
|
|
58
|
+
[self.scroller mountChildComponentView:content index:0];
|
|
59
|
+
UIView *header = [[UIView alloc] initWithFrame:CGRectMake(0, -200, 320, 200)];
|
|
60
|
+
[self.scroller.scrollView addSubview:header];
|
|
61
|
+
auto props = std::make_shared<RNCNativeScrollerProps>();
|
|
62
|
+
props->contentWidth = 320;
|
|
63
|
+
props->contentHeight = 900;
|
|
64
|
+
[self.scroller updateProps:props oldProps:nullptr];
|
|
65
|
+
XCTAssertEqualWithAccuracy(self.scroller.scrollView.contentSize.height, 900, 0.01);
|
|
66
|
+
XCTAssertTrue(CGRectEqualToRect(content.frame, CGRectMake(0, 0, 320, 900)));
|
|
67
|
+
XCTAssertTrue(CGRectEqualToRect(header.frame, CGRectMake(0, -200, 320, 200)));
|
|
68
|
+
XCTAssertEqual(header.superview, self.scroller.scrollView);
|
|
69
|
+
XCTAssertFalse([self.scroller.scrollView isKindOfClass:NSClassFromString(@"RCTEnhancedScrollView")]);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
- (void)testShrinkClampsOnlyUnreachableUpperOffset
|
|
73
|
+
{
|
|
74
|
+
auto props = std::make_shared<RNCNativeScrollerProps>();
|
|
75
|
+
props->contentWidth = 320;
|
|
76
|
+
props->contentHeight = 1500;
|
|
77
|
+
[self.scroller updateProps:props oldProps:nullptr];
|
|
78
|
+
self.scroller.scrollView.contentInset = UIEdgeInsetsMake(200, 0, 468, 0);
|
|
79
|
+
self.scroller.scrollView.contentOffset = CGPointMake(0, 800);
|
|
80
|
+
auto shorter = std::make_shared<RNCNativeScrollerProps>();
|
|
81
|
+
shorter->contentWidth = 320;
|
|
82
|
+
shorter->contentHeight = 132;
|
|
83
|
+
[self.scroller updateProps:shorter oldProps:props];
|
|
84
|
+
[self.scroller clampContentOffsetIfNeeded];
|
|
85
|
+
XCTAssertEqualWithAccuracy(self.scroller.scrollView.contentOffset.y, 0, 0.01);
|
|
86
|
+
self.scroller.scrollView.contentOffset = CGPointMake(0, -240);
|
|
87
|
+
auto empty = std::make_shared<RNCNativeScrollerProps>();
|
|
88
|
+
empty->contentWidth = 320;
|
|
89
|
+
empty->contentHeight = 0;
|
|
90
|
+
[self.scroller updateProps:empty oldProps:shorter];
|
|
91
|
+
[self.scroller clampContentOffsetIfNeeded];
|
|
92
|
+
XCTAssertEqualWithAccuracy(self.scroller.scrollView.contentOffset.y, -240, 0.01);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
- (void)testNativeInterruptionEndsEachDragAndMomentumOnlyOnce
|
|
96
|
+
{
|
|
97
|
+
UIScrollView *scroll = self.scroller.scrollView;
|
|
98
|
+
id<UIScrollViewDelegate> delegate = scroll.delegate;
|
|
99
|
+
[delegate scrollViewWillBeginDragging:scroll];
|
|
100
|
+
[self.scroller stopScrolling];
|
|
101
|
+
[delegate scrollViewDidEndDragging:scroll willDecelerate:NO];
|
|
102
|
+
XCTAssertEqualObjects(self.scroller.events, (@[@1, @2]));
|
|
103
|
+
[self.scroller.events removeAllObjects];
|
|
104
|
+
[delegate scrollViewWillBeginDecelerating:scroll];
|
|
105
|
+
[self.scroller stopScrolling];
|
|
106
|
+
[delegate scrollViewDidEndDecelerating:scroll];
|
|
107
|
+
NSPredicate *terminal = [NSPredicate predicateWithFormat:@"SELF == 4"];
|
|
108
|
+
XCTAssertEqual([self.scroller.events filteredArrayUsingPredicate:terminal].count, 1u);
|
|
109
|
+
XCTAssertEqualObjects(self.scroller.events.firstObject, @3);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
- (void)testRefreshReplacementAndRecycleDoNotKeepOldControl
|
|
113
|
+
{
|
|
114
|
+
auto props = std::make_shared<RNCNativeScrollerProps>();
|
|
115
|
+
props->refreshEnabled = true;
|
|
116
|
+
props->refreshProgressViewOffset = 24;
|
|
117
|
+
[self.scroller updateProps:props oldProps:nullptr];
|
|
118
|
+
UIRefreshControl *original = self.scroller.scrollView.refreshControl;
|
|
119
|
+
XCTAssertNotNil(original);
|
|
120
|
+
XCTAssertEqualWithAccuracy(original.bounds.origin.y, -24, 0.01);
|
|
121
|
+
[self.scroller prepareForRecycle];
|
|
122
|
+
XCTAssertNil(self.scroller.scrollView.refreshControl);
|
|
123
|
+
[self.scroller updateProps:props oldProps:props];
|
|
124
|
+
XCTAssertNotNil(self.scroller.scrollView.refreshControl);
|
|
125
|
+
XCTAssertNotEqual(self.scroller.scrollView.refreshControl, original);
|
|
126
|
+
XCTAssertEqualWithAccuracy(self.scroller.scrollView.refreshControl.bounds.origin.y, -24, 0.01);
|
|
127
|
+
[self.scroller setRefreshing:NO];
|
|
128
|
+
XCTAssertFalse(self.scroller.scrollView.refreshControl.refreshing);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
- (void)testCallerInsetUpdatesSurvivePagerReapplyAndRelease
|
|
132
|
+
{
|
|
133
|
+
RNCCollapsiblePagerViewComponentView *pager = [RNCCollapsiblePagerViewComponentView new];
|
|
134
|
+
auto pagerProps = std::make_shared<RNCCollapsiblePagerViewProps>();
|
|
135
|
+
pagerProps->headerHeight = 120;
|
|
136
|
+
pagerProps->stickyHeaderHeight = 40;
|
|
137
|
+
[pager updateProps:pagerProps oldProps:nullptr];
|
|
138
|
+
[pager addSubview:self.scroller];
|
|
139
|
+
auto props = std::make_shared<RNCNativeScrollerProps>();
|
|
140
|
+
props->contentWidth = 320;
|
|
141
|
+
props->contentHeight = 1500;
|
|
142
|
+
props->contentInsetTop = 10;
|
|
143
|
+
[self.scroller updateProps:props oldProps:nullptr];
|
|
144
|
+
[pager applyInsetsToScrollView:self.scroller.scrollView pageIndex:0 restore:NO];
|
|
145
|
+
XCTAssertEqualWithAccuracy(self.scroller.scrollView.contentInset.top, 170, 0.01);
|
|
146
|
+
auto updated = std::make_shared<RNCNativeScrollerProps>();
|
|
147
|
+
updated->contentWidth = 320;
|
|
148
|
+
updated->contentHeight = 1500;
|
|
149
|
+
updated->contentInsetTop = 30;
|
|
150
|
+
updated->contentInsetBottom = 20;
|
|
151
|
+
[self.scroller updateProps:updated oldProps:props];
|
|
152
|
+
[pager applyInsetsToScrollView:self.scroller.scrollView pageIndex:0 restore:NO];
|
|
153
|
+
XCTAssertEqualWithAccuracy(self.scroller.scrollView.contentInset.top, 190, 0.01);
|
|
154
|
+
[pager restoreScrollViewState:self.scroller.scrollView resetSavedOffset:YES];
|
|
155
|
+
XCTAssertEqualWithAccuracy(self.scroller.scrollView.contentInset.top, 30, 0.01);
|
|
156
|
+
XCTAssertEqualWithAccuracy(self.scroller.scrollView.contentInset.bottom, 20, 0.01);
|
|
157
|
+
[self.scroller removeFromSuperview];
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
- (void)testRecycledDelegateCannotStartASequenceForTheNextOwner
|
|
161
|
+
{
|
|
162
|
+
UIScrollView *scroll = self.scroller.scrollView;
|
|
163
|
+
id<UIScrollViewDelegate> oldDelegate = scroll.delegate;
|
|
164
|
+
[self.scroller prepareForRecycle];
|
|
165
|
+
XCTAssertNil(scroll.delegate);
|
|
166
|
+
[oldDelegate scrollViewWillBeginDragging:scroll];
|
|
167
|
+
[oldDelegate scrollViewWillBeginDecelerating:scroll];
|
|
168
|
+
auto props = std::make_shared<RNCNativeScrollerProps>();
|
|
169
|
+
[self.scroller updateProps:props oldProps:nullptr];
|
|
170
|
+
XCTAssertEqual(scroll.delegate, self.scroller);
|
|
171
|
+
[self.scroller stopScrolling];
|
|
172
|
+
NSPredicate *terminal = [NSPredicate predicateWithFormat:@"SELF == 2 OR SELF == 4"];
|
|
173
|
+
XCTAssertEqual([self.scroller.events filteredArrayUsingPredicate:terminal].count, 0u);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
- (void)testPagerExplicitlyFindsIndependentScrollView
|
|
177
|
+
{
|
|
178
|
+
RNCCollapsiblePagerViewComponentView *pager = [RNCCollapsiblePagerViewComponentView new];
|
|
179
|
+
UIView *page = [UIView new];
|
|
180
|
+
[page addSubview:self.scroller];
|
|
181
|
+
SEL finder = NSSelectorFromString(@"findExplicitNativeScrollerInView:");
|
|
182
|
+
UIScrollView *(*find)(id, SEL, UIView *) =
|
|
183
|
+
(UIScrollView *(*)(id, SEL, UIView *))[pager methodForSelector:finder];
|
|
184
|
+
XCTAssertEqual(find(pager, finder, page), self.scroller.scrollView);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
- (void)testContentViewportUsesOnlyCallerInsetsAndStaticStickyHeight
|
|
188
|
+
{
|
|
189
|
+
RNCCollapsiblePagerViewComponentView *pager = [RNCCollapsiblePagerViewComponentView new];
|
|
190
|
+
auto pagerProps = std::make_shared<RNCCollapsiblePagerViewProps>();
|
|
191
|
+
pagerProps->headerHeight = 120;
|
|
192
|
+
pagerProps->stickyHeaderHeight = 40;
|
|
193
|
+
[pager updateProps:pagerProps oldProps:nullptr];
|
|
194
|
+
[pager addSubview:self.scroller];
|
|
195
|
+
auto props = std::make_shared<RNCNativeScrollerProps>();
|
|
196
|
+
props->contentWidth = 320;
|
|
197
|
+
props->contentHeight = 20;
|
|
198
|
+
props->contentInsetTop = 10;
|
|
199
|
+
props->contentInsetBottom = 20;
|
|
200
|
+
[self.scroller updateProps:props oldProps:nullptr];
|
|
201
|
+
[pager applyInsetsToScrollView:self.scroller.scrollView pageIndex:0 restore:NO];
|
|
202
|
+
XCTAssertEqualWithAccuracy([self.scroller contentViewportHeight], 530, 0.01);
|
|
203
|
+
// Synthetic bottom fill and temporary refresh insets are not caller geometry.
|
|
204
|
+
self.scroller.scrollView.contentInset = UIEdgeInsetsMake(230, 0, 510, 0);
|
|
205
|
+
XCTAssertEqualWithAccuracy([self.scroller contentViewportHeight], 530, 0.01);
|
|
206
|
+
auto filled = std::make_shared<RNCNativeScrollerProps>();
|
|
207
|
+
filled->contentWidth = 320;
|
|
208
|
+
filled->contentHeight = 530;
|
|
209
|
+
filled->contentInsetTop = 10;
|
|
210
|
+
filled->contentInsetBottom = 20;
|
|
211
|
+
[self.scroller updateProps:filled oldProps:props];
|
|
212
|
+
XCTAssertEqualWithAccuracy([self.scroller contentViewportHeight], 530, 0.01);
|
|
213
|
+
LayoutMetrics metrics;
|
|
214
|
+
metrics.frame.size = {320, 700};
|
|
215
|
+
[self.scroller updateLayoutMetrics:metrics oldLayoutMetrics:{}];
|
|
216
|
+
XCTAssertEqualWithAccuracy([self.scroller contentViewportHeight], 630, 0.01);
|
|
217
|
+
auto updated = std::make_shared<RNCNativeScrollerProps>();
|
|
218
|
+
updated->contentWidth = 320;
|
|
219
|
+
updated->contentHeight = 530;
|
|
220
|
+
updated->contentInsetTop = 10;
|
|
221
|
+
updated->contentInsetBottom = 30;
|
|
222
|
+
[self.scroller updateProps:updated oldProps:filled];
|
|
223
|
+
XCTAssertEqualWithAccuracy([self.scroller contentViewportHeight], 620, 0.01);
|
|
224
|
+
auto tallerSticky = std::make_shared<RNCCollapsiblePagerViewProps>();
|
|
225
|
+
tallerSticky->headerHeight = 250;
|
|
226
|
+
tallerSticky->stickyHeaderHeight = 60;
|
|
227
|
+
[pager updateProps:tallerSticky oldProps:pagerProps];
|
|
228
|
+
[pager applyInsetsToScrollView:self.scroller.scrollView pageIndex:0 restore:NO];
|
|
229
|
+
XCTAssertEqualWithAccuracy([self.scroller contentViewportHeight], 600, 0.01);
|
|
230
|
+
[pager restoreScrollViewState:self.scroller.scrollView resetSavedOffset:YES];
|
|
231
|
+
XCTAssertEqualWithAccuracy([self.scroller contentViewportHeight], 660, 0.01);
|
|
232
|
+
[self.scroller removeFromSuperview];
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
- (void)testContentViewportReleasesIndependentPagerContributions
|
|
236
|
+
{
|
|
237
|
+
NSObject *outer = [NSObject new];
|
|
238
|
+
NSObject *inner = [NSObject new];
|
|
239
|
+
[self.scroller setPagerStickyHeight:40 forOwner:outer];
|
|
240
|
+
[self.scroller setPagerStickyHeight:20 forOwner:inner];
|
|
241
|
+
XCTAssertEqualWithAccuracy([self.scroller contentViewportHeight], 540, 0.01);
|
|
242
|
+
[self.scroller setPagerStickyHeight:40 forOwner:outer];
|
|
243
|
+
XCTAssertEqualWithAccuracy([self.scroller contentViewportHeight], 540, 0.01);
|
|
244
|
+
[self.scroller setPagerStickyHeight:0 forOwner:outer];
|
|
245
|
+
XCTAssertEqualWithAccuracy([self.scroller contentViewportHeight], 580, 0.01);
|
|
246
|
+
[self.scroller prepareForRecycle];
|
|
247
|
+
auto props = std::make_shared<RNCNativeScrollerProps>();
|
|
248
|
+
[self.scroller updateProps:props oldProps:nullptr];
|
|
249
|
+
XCTAssertEqualWithAccuracy([self.scroller contentViewportHeight], 600, 0.01);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
@end
|
|
@@ -30,6 +30,10 @@ export class CollapsiblePagerView extends React.PureComponent {
|
|
|
30
30
|
lastMountSignature = null;
|
|
31
31
|
nativeTabCommandId = 0;
|
|
32
32
|
latestNativeTabTarget = null;
|
|
33
|
+
|
|
34
|
+
// Target of the last tab command native has received and not yet reported
|
|
35
|
+
// as selected. Commands superseded before their frame never reach native.
|
|
36
|
+
dispatchedNativeTabTarget = null;
|
|
33
37
|
latestNativeSelectedPage = this.state.selectedPage;
|
|
34
38
|
nativePagerScrollState = "idle";
|
|
35
39
|
nativeTabCommandQueuedDuringScroll = false;
|
|
@@ -89,6 +93,7 @@ export class CollapsiblePagerView extends React.PureComponent {
|
|
|
89
93
|
if (commandId !== this.nativeTabCommandId || !this.nativeRef) {
|
|
90
94
|
return;
|
|
91
95
|
}
|
|
96
|
+
this.dispatchedNativeTabTarget = position;
|
|
92
97
|
if (animated) {
|
|
93
98
|
CollapsiblePagerViewNativeCommands.setPage(this.nativeRef, position);
|
|
94
99
|
} else {
|
|
@@ -136,6 +141,7 @@ export class CollapsiblePagerView extends React.PureComponent {
|
|
|
136
141
|
const transitionComplete = pendingTarget === position;
|
|
137
142
|
if (transitionComplete) {
|
|
138
143
|
this.latestNativeTabTarget = null;
|
|
144
|
+
this.dispatchedNativeTabTarget = null;
|
|
139
145
|
this.nativeTabCommandQueuedDuringScroll = false;
|
|
140
146
|
}
|
|
141
147
|
this.setState({
|
|
@@ -154,6 +160,7 @@ export class CollapsiblePagerView extends React.PureComponent {
|
|
|
154
160
|
if (scrollState === "idle" && this.latestNativeTabTarget !== null && this.latestNativeTabTarget !== this.latestNativeSelectedPage && !queuedDuringPreviousScroll) {
|
|
155
161
|
++this.nativeTabCommandId;
|
|
156
162
|
this.latestNativeTabTarget = null;
|
|
163
|
+
this.dispatchedNativeTabTarget = null;
|
|
157
164
|
this.setState({
|
|
158
165
|
transientRetainedPages: []
|
|
159
166
|
}, this.notifyMountedPagesChanged);
|
|
@@ -166,7 +173,9 @@ export class CollapsiblePagerView extends React.PureComponent {
|
|
|
166
173
|
onNativeTabPress = event => {
|
|
167
174
|
const position = pageIndex(event.nativeEvent.position);
|
|
168
175
|
if (position !== null && position >= 0 && position < this.pages().length && (position !== this.state.selectedPage || this.latestNativeTabTarget !== null)) {
|
|
169
|
-
|
|
176
|
+
// Selection state lags an in-flight transition until native reports it.
|
|
177
|
+
const currentPage = this.dispatchedNativeTabTarget ?? this.state.selectedPage;
|
|
178
|
+
this.dispatchNativeTabPageCommand(position, this.props.nativeTabPressAnimationEnabled ?? Math.abs(position - currentPage) <= 1);
|
|
170
179
|
}
|
|
171
180
|
this.props.onNativeTabPress?.(event);
|
|
172
181
|
};
|