@onekeyfe/react-native-pager-view 3.0.173 → 3.0.175
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/android/src/main/java/com/reactnativepagerview/CollapsiblePagerHost.kt +860 -25
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerViewManager.kt +17 -0
- package/android/src/main/java/com/reactnativepagerview/NativeScrollerView.kt +16 -0
- package/android/src/main/java/com/reactnativepagerview/event/CollapsibleStateChangedEvent.kt +4 -0
- package/android/src/main/res/values/native_scroll_coordinator_ids.xml +2 -0
- package/android/src/test/java/com/reactnativepagerview/NativeScrollerPendingRestoreTest.kt +11 -0
- package/docs/SPEC.md +65 -1
- package/ios/RNCCollapsiblePagerViewComponentView.mm +616 -30
- package/ios/RNCNativeScrollerComponentView.h +1 -0
- package/ios/RNCNativeScrollerComponentView.mm +48 -0
- package/ios/tests/RNCCollapsiblePagerFirstFrameTests.mm +332 -0
- package/ios/tests/RNCCollapsiblePagerRefreshControlTests.mm +14 -5
- package/lib/module/CollapsiblePagerView.js +6 -0
- package/lib/module/CollapsiblePagerView.web.js +81 -0
- package/lib/module/CollapsiblePagerViewNativeComponent.ts +8 -0
- package/lib/typescript/src/CollapsiblePagerView.d.ts +6 -0
- package/lib/typescript/src/CollapsiblePagerView.web.d.ts +11 -0
- package/lib/typescript/src/CollapsiblePagerViewNativeComponent.d.ts +3 -0
- package/package.json +2 -2
- package/src/CollapsiblePagerView.tsx +18 -0
- package/src/CollapsiblePagerView.web.tsx +170 -36
- package/src/CollapsiblePagerViewNativeComponent.ts +8 -0
|
@@ -10,6 +10,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
10
10
|
|
|
11
11
|
// Native inset ownership stays separate from Fabric/Yoga content layout.
|
|
12
12
|
@protocol RNCNativeScrollerInsetCoordinator <NSObject>
|
|
13
|
+
- (void)nativeScrollerWillReceiveScrollCommand:(RNCNativeScrollerComponentView *)scroller;
|
|
13
14
|
- (void)nativeScroller:(RNCNativeScrollerComponentView *)scroller
|
|
14
15
|
callerInsetChangedFrom:(UIEdgeInsets)previous to:(UIEdgeInsets)next;
|
|
15
16
|
@end
|
|
@@ -33,6 +33,9 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
33
33
|
@end
|
|
34
34
|
|
|
35
35
|
@implementation RNCNativeScrollerComponentView {
|
|
36
|
+
#if DEBUG
|
|
37
|
+
NSUInteger _offsetDiagSequence;
|
|
38
|
+
#endif
|
|
36
39
|
RNCNativeScrollerShadowNode::ConcreteState::Shared _state;
|
|
37
40
|
RNCNativeVerticalScrollView *_scrollView;
|
|
38
41
|
UIView<RCTComponentViewProtocol> *_reactContent;
|
|
@@ -51,6 +54,24 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
51
54
|
NSNumber *_lastContentViewportHeight;
|
|
52
55
|
}
|
|
53
56
|
|
|
57
|
+
- (void)offsetDiag:(NSString *)reason
|
|
58
|
+
{
|
|
59
|
+
#if DEBUG
|
|
60
|
+
Class logClass = NSClassFromString(@"ReactNativeNativeLogger.OneKeyLog") ?: NSClassFromString(@"OneKeyLog");
|
|
61
|
+
SEL selector = NSSelectorFromString(@"debug::");
|
|
62
|
+
if (logClass == nil || ![logClass respondsToSelector:selector]) return;
|
|
63
|
+
NSString *message = [NSString stringWithFormat:
|
|
64
|
+
@"[HOME-OFFSET-DIAG] scroller phase=%@ seq=%lu wallMs=%.0f mono=%.6f host=%p tag=%ld scroll=%p generation=%lu physicalY=%.4f insetTop=%.4f logicalY=%.4f contentHeight=%.4f boundsHeight=%.4f adjustedBottom=%.4f needsClamp=%d drag=%d decel=%d refresh=%d",
|
|
65
|
+
reason, (unsigned long)++_offsetDiagSequence, NSDate.date.timeIntervalSince1970 * 1000, CACurrentMediaTime(),
|
|
66
|
+
(__bridge void *)self, (long)self.tag, (__bridge void *)_scrollView, (unsigned long)_generation,
|
|
67
|
+
_scrollView.contentOffset.y, _scrollView.contentInset.top, _scrollView.contentOffset.y + _scrollView.contentInset.top,
|
|
68
|
+
_scrollView.contentSize.height, _scrollView.bounds.size.height, _scrollView.adjustedContentInset.bottom,
|
|
69
|
+
_needsContentClamp, _scrollView.dragging, _scrollView.decelerating, _refreshControl.refreshing];
|
|
70
|
+
typedef void (*LogFunction)(id, SEL, NSString *, NSString *);
|
|
71
|
+
((LogFunction)[logClass methodForSelector:selector])(logClass, selector, @"NativeScroller", message);
|
|
72
|
+
#endif
|
|
73
|
+
}
|
|
74
|
+
|
|
54
75
|
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
|
55
76
|
{
|
|
56
77
|
return concreteComponentDescriptorProvider<RNCNativeScrollerComponentDescriptor>();
|
|
@@ -179,7 +200,9 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
179
200
|
_needsContentClamp |= size.height < _scrollView.contentSize.height;
|
|
180
201
|
CGPoint offset = _scrollView.contentOffset;
|
|
181
202
|
BOOL wasPulling = offset.y < -_scrollView.adjustedContentInset.top;
|
|
203
|
+
[self offsetDiag:@"content-size-before"];
|
|
182
204
|
_scrollView.contentSize = size;
|
|
205
|
+
[self offsetDiag:@"content-size-after"];
|
|
183
206
|
// UIKit can synchronously discard a pull when content becomes empty.
|
|
184
207
|
// Upper-range shrink correction remains owned by the deferred clamp.
|
|
185
208
|
if (wasPulling && _scrollView.contentOffset.y != offset.y) {
|
|
@@ -197,10 +220,12 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
197
220
|
- (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics
|
|
198
221
|
oldLayoutMetrics:(const LayoutMetrics &)oldLayoutMetrics
|
|
199
222
|
{
|
|
223
|
+
[self offsetDiag:@"layout-before"];
|
|
200
224
|
CGSize previous = _scrollView.bounds.size;
|
|
201
225
|
[super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
|
|
202
226
|
[self updateFabricContentOrigin];
|
|
203
227
|
[self emitContentViewportChangeIfNeeded];
|
|
228
|
+
[self offsetDiag:@"layout-after"];
|
|
204
229
|
if (!CGSizeEqualToSize(previous, _scrollView.bounds.size)) {
|
|
205
230
|
_needsContentClamp = YES;
|
|
206
231
|
[self scheduleContentClamp];
|
|
@@ -263,6 +288,7 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
263
288
|
- (void)scheduleContentClamp
|
|
264
289
|
{
|
|
265
290
|
if (!_needsContentClamp) return;
|
|
291
|
+
[self offsetDiag:@"clamp-scheduled"];
|
|
266
292
|
NSUInteger generation = _generation;
|
|
267
293
|
__weak __typeof__(self) weakSelf = self;
|
|
268
294
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
@@ -274,13 +300,19 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
274
300
|
|
|
275
301
|
- (void)clampContentOffsetIfNeeded
|
|
276
302
|
{
|
|
303
|
+
[self offsetDiag:@"clamp-enter-or-deferred"];
|
|
277
304
|
if (!_needsContentClamp || _scrollView.dragging || _scrollView.decelerating ||
|
|
278
305
|
_refreshControl.refreshing || _scrollView.bounds.size.height <= 0) return;
|
|
279
306
|
_needsContentClamp = NO;
|
|
280
307
|
CGFloat maximum = MAX(-_scrollView.adjustedContentInset.top,
|
|
281
308
|
_scrollView.contentSize.height - _scrollView.bounds.size.height + _scrollView.adjustedContentInset.bottom);
|
|
309
|
+
#if DEBUG
|
|
310
|
+
[self offsetDiag:[NSString stringWithFormat:@"clamp-candidate-max=%.4f", maximum]];
|
|
311
|
+
#endif
|
|
282
312
|
if (_scrollView.contentOffset.y > maximum) {
|
|
313
|
+
[self offsetDiag:@"clamp-applied-before"];
|
|
283
314
|
[_scrollView setContentOffset:CGPointMake(_scrollView.contentOffset.x, maximum) animated:NO];
|
|
315
|
+
[self offsetDiag:@"clamp-applied-after"];
|
|
284
316
|
}
|
|
285
317
|
}
|
|
286
318
|
|
|
@@ -313,12 +345,15 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
313
345
|
{
|
|
314
346
|
if (!_momentumActive) return;
|
|
315
347
|
_momentumActive = NO;
|
|
348
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:@"OneKeyNativeListScrollCommandAccepted"
|
|
349
|
+
object:_scrollView userInfo:@{@"phase": @2}];
|
|
316
350
|
[self emitScrollEvent:4 velocity:CGPointZero target:_scrollView.contentOffset];
|
|
317
351
|
}
|
|
318
352
|
|
|
319
353
|
- (void)stopScrolling
|
|
320
354
|
{
|
|
321
355
|
if (_stopping) return;
|
|
356
|
+
[self offsetDiag:@"stop-before"];
|
|
322
357
|
_stopping = YES;
|
|
323
358
|
[_scrollView setContentOffset:_scrollView.contentOffset animated:NO];
|
|
324
359
|
if (_dragActive) {
|
|
@@ -330,6 +365,7 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
330
365
|
}
|
|
331
366
|
[self finishMomentum];
|
|
332
367
|
_stopping = NO;
|
|
368
|
+
[self offsetDiag:@"stop-after"];
|
|
333
369
|
}
|
|
334
370
|
|
|
335
371
|
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
|
|
@@ -394,6 +430,14 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
394
430
|
- (void)scrollTo:(double)x y:(double)y animated:(BOOL)animated
|
|
395
431
|
{
|
|
396
432
|
if (_recycled || !isfinite(x) || !isfinite(y)) return;
|
|
433
|
+
for (UIView *owner = self.superview; owner != nil; owner = owner.superview) {
|
|
434
|
+
if ([owner conformsToProtocol:@protocol(RNCNativeScrollerInsetCoordinator)]) {
|
|
435
|
+
[(id<RNCNativeScrollerInsetCoordinator>)owner nativeScrollerWillReceiveScrollCommand:self];
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
#if DEBUG
|
|
439
|
+
[self offsetDiag:[NSString stringWithFormat:@"command-scrollTo-enter-x=%.4f-y=%.4f-animated=%d", x, y, animated]];
|
|
440
|
+
#endif
|
|
397
441
|
[self stopScrolling];
|
|
398
442
|
UIEdgeInsets inset = _scrollView.adjustedContentInset;
|
|
399
443
|
CGPoint target = CGPointMake(MAX(-inset.left, MIN(x,
|
|
@@ -401,11 +445,15 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
401
445
|
MAX(-inset.top, MIN(y,
|
|
402
446
|
MAX(-inset.top, _scrollView.contentSize.height - _scrollView.bounds.size.height + inset.bottom))));
|
|
403
447
|
if (CGPointEqualToPoint(target, _scrollView.contentOffset)) return;
|
|
448
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:@"OneKeyNativeListScrollCommandAccepted"
|
|
449
|
+
object:_scrollView userInfo:@{@"phase": @1}];
|
|
404
450
|
if (animated) {
|
|
405
451
|
_momentumActive = YES;
|
|
406
452
|
[self emitScrollEvent:3 velocity:CGPointZero target:target];
|
|
407
453
|
}
|
|
408
454
|
[_scrollView setContentOffset:target animated:animated];
|
|
455
|
+
if (!animated) [[NSNotificationCenter defaultCenter] postNotificationName:@"OneKeyNativeListScrollCommandAccepted"
|
|
456
|
+
object:_scrollView userInfo:@{@"phase": @2}];
|
|
409
457
|
}
|
|
410
458
|
|
|
411
459
|
- (void)scrollToEnd:(BOOL)animated
|
|
@@ -6,11 +6,27 @@
|
|
|
6
6
|
using namespace facebook::react;
|
|
7
7
|
|
|
8
8
|
@interface RNCCollapsiblePagerViewComponentView (FirstFrameTesting)
|
|
9
|
+
- (void)mountingTransactionWillMount:(const MountingTransaction &)transaction
|
|
10
|
+
withSurfaceTelemetry:(const SurfaceTelemetry &)telemetry;
|
|
9
11
|
- (void)mountingTransactionDidMount:(const MountingTransaction &)transaction
|
|
10
12
|
withSurfaceTelemetry:(const SurfaceTelemetry &)telemetry;
|
|
11
13
|
- (UIScrollView *)findExplicitNativeScrollerInView:(UIView *)view;
|
|
12
14
|
- (UIScrollView *)findVerticalScrollViewInView:(UIView *)view;
|
|
13
15
|
- (void)attachScrollObserverForCurrentPage;
|
|
16
|
+
- (void)attachScrollObserverForCurrentPageAfterTransition:(BOOL)selectionSettled;
|
|
17
|
+
- (void)detachScrollObserver;
|
|
18
|
+
- (void)beginPageStateTransition;
|
|
19
|
+
- (void)updatePageStateTransitionProgress:(CGFloat)progress;
|
|
20
|
+
- (void)finishPageStateTransition;
|
|
21
|
+
- (void)scrollToTop:(NSInteger)target animated:(BOOL)animated;
|
|
22
|
+
- (void)updateHeaderSettledStateForceBusy:(BOOL)forceBusy;
|
|
23
|
+
- (void)preparePageForHorizontalTransitionAtIndex:(NSInteger)index;
|
|
24
|
+
- (void)applyInsetsToScrollView:(UIScrollView *)scrollView pageIndex:(NSInteger)index restore:(BOOL)restore;
|
|
25
|
+
@end
|
|
26
|
+
|
|
27
|
+
@interface RNCNativeScrollerComponentView (ScrollCommandTesting)
|
|
28
|
+
- (void)scrollTo:(double)x y:(double)y animated:(BOOL)animated;
|
|
29
|
+
- (void)scrollToEnd:(BOOL)animated;
|
|
14
30
|
@end
|
|
15
31
|
|
|
16
32
|
@interface RNCCollapsiblePagerFirstFrameTests : XCTestCase
|
|
@@ -18,6 +34,322 @@ using namespace facebook::react;
|
|
|
18
34
|
|
|
19
35
|
@implementation RNCCollapsiblePagerFirstFrameTests
|
|
20
36
|
|
|
37
|
+
- (void)testHeaderSettledAtTopRequiresCurrentQuietOwnerAndCancelsDetachedTimer
|
|
38
|
+
{
|
|
39
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:1];
|
|
40
|
+
// The fixture mounts page 0 directly; UIKit must not select it again.
|
|
41
|
+
[pager setValue:@YES forKey:@"hasAppliedInitialPage"];
|
|
42
|
+
UIView *header = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 120)];
|
|
43
|
+
[pager addSubview:header];
|
|
44
|
+
[pager setValue:header forKey:@"headerView"];
|
|
45
|
+
RNCNativeScrollerComponentView *scroller = [self addScrollerToPager:pager atIndex:0];
|
|
46
|
+
[pager.window layoutIfNeeded];
|
|
47
|
+
[pager layoutIfNeeded];
|
|
48
|
+
XCTestExpectation *initialLayout = [self expectationWithDescription:@"Initial observer attachment drained"];
|
|
49
|
+
dispatch_async(dispatch_get_main_queue(), ^{ [initialLayout fulfill]; });
|
|
50
|
+
[self waitForExpectations:@[initialLayout] timeout:1];
|
|
51
|
+
[pager detachScrollObserver];
|
|
52
|
+
[pager attachScrollObserverForCurrentPage];
|
|
53
|
+
[pager updateHeaderSettledStateForceBusy:NO];
|
|
54
|
+
XCTAssertFalse([[pager valueForKey:@"headerSettledAtTop"] boolValue]);
|
|
55
|
+
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]];
|
|
56
|
+
XCTAssertTrue([[pager valueForKey:@"headerSettledAtTop"] boolValue]);
|
|
57
|
+
|
|
58
|
+
[scroller scrollTo:0 y:200 - scroller.scrollView.contentInset.top animated:NO];
|
|
59
|
+
XCTAssertFalse([[pager valueForKey:@"headerSettledAtTop"] boolValue]);
|
|
60
|
+
[scroller scrollTo:0 y:-scroller.scrollView.adjustedContentInset.top animated:NO];
|
|
61
|
+
[pager updateHeaderSettledStateForceBusy:NO];
|
|
62
|
+
XCTAssertFalse([[pager valueForKey:@"headerSettledAtTop"] boolValue]);
|
|
63
|
+
[pager detachScrollObserver];
|
|
64
|
+
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]];
|
|
65
|
+
XCTAssertFalse([[pager valueForKey:@"headerSettledAtTop"] boolValue]);
|
|
66
|
+
XCTAssertNil([pager valueForKey:@"headerSettledTimer"]);
|
|
67
|
+
|
|
68
|
+
[pager attachScrollObserverForCurrentPage];
|
|
69
|
+
[pager updateHeaderSettledStateForceBusy:NO];
|
|
70
|
+
XCTAssertFalse([[pager valueForKey:@"headerSettledAtTop"] boolValue]);
|
|
71
|
+
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]];
|
|
72
|
+
XCTAssertTrue([[pager valueForKey:@"headerSettledAtTop"] boolValue]);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
- (void)testReachableTopCommandBeforeLayoutMustNotReplayPendingPagePosition
|
|
76
|
+
{
|
|
77
|
+
for (NSNumber *command in @[@0, @1, @2]) {
|
|
78
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:1];
|
|
79
|
+
RNCNativeScrollerComponentView *scroller = [self addScrollerToPager:pager atIndex:0];
|
|
80
|
+
NSMutableDictionary *offsets = [pager valueForKey:@"pageOffsets"];
|
|
81
|
+
offsets[@"page-0"] = @660;
|
|
82
|
+
[pager setValue:@120 forKey:@"headerOffset"];
|
|
83
|
+
scroller.scrollView.contentSize = CGSizeMake(0, 0);
|
|
84
|
+
[pager attachScrollObserverForCurrentPage];
|
|
85
|
+
XCTAssertTrue([[pager valueForKey:@"pendingPageRestoreKeys"] containsObject:@"page-0"]);
|
|
86
|
+
XCTAssertEqualObjects(offsets[@"page-0"], @660);
|
|
87
|
+
XCTAssertEqual([pager valueForKey:@"observedScrollView"], scroller.scrollView);
|
|
88
|
+
XCTAssertEqualObjects([pager valueForKey:@"observedPageKey"], @"page-0");
|
|
89
|
+
XCTAssertEqualObjects([pager valueForKey:@"currentIndex"], @0);
|
|
90
|
+
if (command.boolValue) {
|
|
91
|
+
if (command.integerValue == 2) {
|
|
92
|
+
CGFloat reachableEndQ = MAX(-scroller.scrollView.adjustedContentInset.top,
|
|
93
|
+
scroller.scrollView.contentSize.height - scroller.scrollView.bounds.size.height +
|
|
94
|
+
scroller.scrollView.adjustedContentInset.bottom) + scroller.scrollView.contentInset.top;
|
|
95
|
+
XCTAssertEqualWithAccuracy(reachableEndQ, 120, 0.01);
|
|
96
|
+
XCTAssertEqualWithAccuracy(scroller.scrollView.contentOffset.y + scroller.scrollView.contentInset.top, reachableEndQ, 0.01);
|
|
97
|
+
[scroller scrollToEnd:NO];
|
|
98
|
+
}
|
|
99
|
+
else [scroller scrollTo:0 y:-scroller.scrollView.adjustedContentInset.top animated:NO];
|
|
100
|
+
XCTAssertEqualWithAccuracy(scroller.scrollView.contentOffset.y + scroller.scrollView.contentInset.top, command.integerValue == 2 ? 120 : 0, 0.01);
|
|
101
|
+
XCTAssertFalse([[pager valueForKey:@"pendingPageRestoreKeys"] containsObject:@"page-0"]);
|
|
102
|
+
XCTAssertEqualWithAccuracy([offsets[@"page-0"] doubleValue], 0, 0.01);
|
|
103
|
+
XCTAssertEqual([pager valueForKey:@"observedScrollView"], scroller.scrollView);
|
|
104
|
+
XCTAssertEqualObjects([pager valueForKey:@"observedPageKey"], @"page-0");
|
|
105
|
+
}
|
|
106
|
+
// The existing observed content-size path retries the pending restoration.
|
|
107
|
+
scroller.scrollView.contentSize = CGSizeMake(320, 1500);
|
|
108
|
+
XCTAssertFalse([[pager valueForKey:@"pendingPageRestoreKeys"] containsObject:@"page-0"]);
|
|
109
|
+
CGFloat expected = command.integerValue == 2 ? 120 : (command.boolValue ? 0 : 780);
|
|
110
|
+
XCTAssertEqualWithAccuracy(scroller.scrollView.contentOffset.y + scroller.scrollView.contentInset.top,
|
|
111
|
+
expected, 0.01, @"command=%@ must not replay a cancelled reading position", command);
|
|
112
|
+
XCTAssertEqualWithAccuracy([[pager valueForKey:@"headerOffset"] doubleValue], command.integerValue == 1 ? 0 : 120, 0.01);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
- (void)testScrollCommandCancellationRespectsOwnerAndValidity
|
|
117
|
+
{
|
|
118
|
+
for (NSNumber *mode in @[@0, @1, @2]) {
|
|
119
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:2];
|
|
120
|
+
RNCNativeScrollerComponentView *current = [self addScrollerToPager:pager atIndex:0];
|
|
121
|
+
RNCNativeScrollerComponentView *other = [self addScrollerToPager:pager atIndex:1];
|
|
122
|
+
NSMutableDictionary *offsets = [pager valueForKey:@"pageOffsets"];
|
|
123
|
+
offsets[@"page-0"] = @660;
|
|
124
|
+
[pager setValue:@120 forKey:@"headerOffset"];
|
|
125
|
+
offsets[@"page-1"] = @360;
|
|
126
|
+
current.scrollView.contentSize = CGSizeMake(0, 0);
|
|
127
|
+
[pager attachScrollObserverForCurrentPage];
|
|
128
|
+
XCTAssertTrue([[pager valueForKey:@"pendingPageRestoreKeys"] containsObject:@"page-0"]);
|
|
129
|
+
if (mode.integerValue == 0) {
|
|
130
|
+
[current scrollTo:0 y:NAN animated:NO];
|
|
131
|
+
} else if (mode.integerValue == 1) {
|
|
132
|
+
[other scrollTo:0 y:-other.scrollView.adjustedContentInset.top animated:NO];
|
|
133
|
+
} else {
|
|
134
|
+
NSUInteger generation = [[pager valueForKey:@"generation"] unsignedIntegerValue];
|
|
135
|
+
[pager setValue:@(generation + 1) forKey:@"observedPageGeneration"];
|
|
136
|
+
[current scrollTo:0 y:-current.scrollView.adjustedContentInset.top animated:NO];
|
|
137
|
+
}
|
|
138
|
+
XCTAssertTrue([[pager valueForKey:@"pendingPageRestoreKeys"] containsObject:@"page-0"]);
|
|
139
|
+
XCTAssertEqualObjects(offsets[@"page-0"], @660);
|
|
140
|
+
XCTAssertEqualObjects(offsets[@"page-1"], @360);
|
|
141
|
+
}
|
|
142
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:2];
|
|
143
|
+
RNCNativeScrollerComponentView *current = [self addScrollerToPager:pager atIndex:0];
|
|
144
|
+
[self addScrollerToPager:pager atIndex:1];
|
|
145
|
+
NSMutableDictionary *offsets = [pager valueForKey:@"pageOffsets"];
|
|
146
|
+
offsets[@"page-0"] = @0;
|
|
147
|
+
offsets[@"page-1"] = @360;
|
|
148
|
+
[pager setValue:@120 forKey:@"headerOffset"];
|
|
149
|
+
[pager attachScrollObserverForCurrentPage];
|
|
150
|
+
XCTAssertFalse([[pager valueForKey:@"pendingPageRestoreKeys"] containsObject:@"page-0"]);
|
|
151
|
+
[current scrollTo:0 y:200 - current.scrollView.contentInset.top animated:NO];
|
|
152
|
+
XCTAssertEqualWithAccuracy(current.scrollView.contentOffset.y + current.scrollView.contentInset.top, 200, 0.01);
|
|
153
|
+
XCTAssertEqualWithAccuracy([offsets[@"page-0"] doubleValue], 80, 0.01);
|
|
154
|
+
XCTAssertEqualObjects(offsets[@"page-1"], @360);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
- (void)testPagerTopCommandsClearOnlyCurrentReadingMemoryInBothModes
|
|
158
|
+
{
|
|
159
|
+
for (NSNumber *smooth in @[@NO, @YES]) {
|
|
160
|
+
for (NSNumber *target in @[@0, @1]) {
|
|
161
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:2];
|
|
162
|
+
[pager setValue:smooth forKey:@"nativeSmoothHeaderScrollEnabled"];
|
|
163
|
+
RNCNativeScrollerComponentView *current = [self addScrollerToPager:pager atIndex:0];
|
|
164
|
+
[self addScrollerToPager:pager atIndex:1];
|
|
165
|
+
NSMutableDictionary *offsets = [pager valueForKey:@"pageOffsets"];
|
|
166
|
+
offsets[@"page-0"] = smooth.boolValue ? @660 : @780;
|
|
167
|
+
offsets[@"page-1"] = @360;
|
|
168
|
+
[pager setValue:@120 forKey:@"headerOffset"];
|
|
169
|
+
[pager attachScrollObserverForCurrentPage];
|
|
170
|
+
[pager scrollToTop:target.integerValue animated:NO];
|
|
171
|
+
CGFloat expectedQ = target.boolValue ? 120 : 0;
|
|
172
|
+
XCTAssertEqualWithAccuracy(current.scrollView.contentOffset.y + current.scrollView.contentInset.top, expectedQ, 0.01);
|
|
173
|
+
XCTAssertEqualWithAccuracy([[pager valueForKey:@"headerOffset"] doubleValue], expectedQ, 0.01);
|
|
174
|
+
XCTAssertEqualWithAccuracy([offsets[@"page-0"] doubleValue], smooth.boolValue ? 0 : expectedQ, 0.01);
|
|
175
|
+
XCTAssertEqualObjects(offsets[@"page-1"], @360);
|
|
176
|
+
XCTAssertFalse([[pager valueForKey:@"pendingPageRestoreKeys"] containsObject:@"page-0"]);
|
|
177
|
+
XCTAssertNil([pager valueForKey:@"pagerTopScrollView"]);
|
|
178
|
+
// A same-target call cannot resurrect the old reading position.
|
|
179
|
+
[pager scrollToTop:target.integerValue animated:NO];
|
|
180
|
+
XCTAssertEqualWithAccuracy(current.scrollView.contentOffset.y + current.scrollView.contentInset.top, expectedQ, 0.01);
|
|
181
|
+
XCTAssertEqualObjects(offsets[@"page-1"], @360);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
- (void)testTransitionLayoutCannotSaveSourceOffsetIntoDestinationCache
|
|
187
|
+
{
|
|
188
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:2];
|
|
189
|
+
RNCNativeScrollerComponentView *source = [self addScrollerToPager:pager atIndex:0];
|
|
190
|
+
RNCNativeScrollerComponentView *target = [self addScrollerToPager:pager atIndex:1];
|
|
191
|
+
NSMutableDictionary *offsets = [pager valueForKey:@"pageOffsets"];
|
|
192
|
+
offsets[@"page-0"] = @0;
|
|
193
|
+
offsets[@"page-1"] = @660;
|
|
194
|
+
[pager setValue:@120 forKey:@"headerOffset"];
|
|
195
|
+
[pager attachScrollObserverForCurrentPage];
|
|
196
|
+
XCTAssertEqualWithAccuracy(source.scrollView.contentOffset.y + source.scrollView.contentInset.top, 120, 0.01);
|
|
197
|
+
XCTAssertEqualObjects([pager valueForKey:@"headerOffset"], @120);
|
|
198
|
+
XCTAssertEqualObjects(offsets[@"page-1"], @660);
|
|
199
|
+
[pager detachScrollObserver];
|
|
200
|
+
[pager setValue:@YES forKey:@"transitioning"];
|
|
201
|
+
// A layout pass runs between programmatic transition start and completion.
|
|
202
|
+
[pager attachScrollObserverForCurrentPage];
|
|
203
|
+
[pager setValue:@1 forKey:@"currentIndex"];
|
|
204
|
+
[pager attachScrollObserverForCurrentPageAfterTransition:YES];
|
|
205
|
+
XCTAssertEqualWithAccuracy([offsets[@"page-1"] doubleValue], 660, 0.01);
|
|
206
|
+
XCTAssertEqualWithAccuracy(target.scrollView.contentOffset.y + target.scrollView.contentInset.top, 780, 0.01);
|
|
207
|
+
XCTAssertNotEqual([pager valueForKey:@"observedScrollView"], source.scrollView);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
- (void)testSmoothTransitionKeepsGlobalHeaderAndIndependentBodyMemory
|
|
211
|
+
{
|
|
212
|
+
for (NSNumber *collapse in @[@0, @60, @120]) {
|
|
213
|
+
for (NSNumber *body in @[@0, @60, @660]) {
|
|
214
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:2];
|
|
215
|
+
RNCNativeScrollerComponentView *source = [self addScrollerToPager:pager atIndex:0];
|
|
216
|
+
RNCNativeScrollerComponentView *target = [self addScrollerToPager:pager atIndex:1];
|
|
217
|
+
NSMutableDictionary *offsets = [pager valueForKey:@"pageOffsets"];
|
|
218
|
+
offsets[@"page-0"] = @0;
|
|
219
|
+
offsets[@"page-1"] = body;
|
|
220
|
+
[pager setValue:collapse forKey:@"headerOffset"];
|
|
221
|
+
[pager attachScrollObserverForCurrentPage];
|
|
222
|
+
[pager beginPageStateTransition];
|
|
223
|
+
[pager setValue:@YES forKey:@"isPagerDragging"];
|
|
224
|
+
[pager setValue:@YES forKey:@"transitioning"];
|
|
225
|
+
[pager updatePageStateTransitionProgress:0.5];
|
|
226
|
+
CGFloat targetQ = collapse.doubleValue + (collapse.doubleValue == 120 ? body.doubleValue : 0);
|
|
227
|
+
XCTAssertEqualWithAccuracy([[pager valueForKey:@"headerOffset"] doubleValue], collapse.doubleValue, 0.01);
|
|
228
|
+
XCTAssertEqualWithAccuracy(source.scrollView.contentOffset.y + source.scrollView.contentInset.top, collapse.doubleValue, 0.01);
|
|
229
|
+
XCTAssertEqualWithAccuracy(target.scrollView.contentOffset.y + target.scrollView.contentInset.top, targetQ, 0.01);
|
|
230
|
+
XCTAssertEqualObjects(offsets[@"page-0"], @0);
|
|
231
|
+
XCTAssertEqualObjects(offsets[@"page-1"], body);
|
|
232
|
+
// Cancellation restores the source display without consuming either reading memory.
|
|
233
|
+
[pager finishPageStateTransition];
|
|
234
|
+
XCTAssertEqualWithAccuracy(source.scrollView.contentOffset.y + source.scrollView.contentInset.top, collapse.doubleValue, 0.01);
|
|
235
|
+
XCTAssertEqualObjects(offsets[@"page-1"], body);
|
|
236
|
+
[pager setValue:@NO forKey:@"isPagerDragging"];
|
|
237
|
+
[pager setValue:@NO forKey:@"transitioning"];
|
|
238
|
+
[pager attachScrollObserverForCurrentPage];
|
|
239
|
+
[pager beginPageStateTransition];
|
|
240
|
+
[pager setValue:@1 forKey:@"destinationIndex"];
|
|
241
|
+
[pager preparePageForHorizontalTransitionAtIndex:1];
|
|
242
|
+
[pager updatePageStateTransitionProgress:1];
|
|
243
|
+
XCTAssertEqualWithAccuracy(source.scrollView.contentOffset.y + source.scrollView.contentInset.top, collapse.doubleValue, 0.01);
|
|
244
|
+
[pager setValue:@1 forKey:@"currentIndex"];
|
|
245
|
+
[pager attachScrollObserverForCurrentPageAfterTransition:YES];
|
|
246
|
+
XCTAssertEqualWithAccuracy(target.scrollView.contentOffset.y + target.scrollView.contentInset.top, targetQ, 0.01);
|
|
247
|
+
XCTAssertEqualWithAccuracy([[pager valueForKey:@"headerOffset"] doubleValue], collapse.doubleValue, 0.01);
|
|
248
|
+
XCTAssertEqualObjects(offsets[@"page-1"], body);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
- (void)testNonAdjacentTransitionUsesFixedEndpointsAndRestoresIntermediatePage
|
|
254
|
+
{
|
|
255
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:4];
|
|
256
|
+
for (NSInteger index = 0; index < 4; index++) [self addScrollerToPager:pager atIndex:index];
|
|
257
|
+
NSMutableDictionary *offsets = [pager valueForKey:@"pageOffsets"];
|
|
258
|
+
offsets[@"page-0"] = @0;
|
|
259
|
+
offsets[@"page-1"] = @60;
|
|
260
|
+
offsets[@"page-2"] = @360;
|
|
261
|
+
offsets[@"page-3"] = @660;
|
|
262
|
+
[pager setValue:@60 forKey:@"headerOffset"];
|
|
263
|
+
[pager attachScrollObserverForCurrentPage];
|
|
264
|
+
[pager beginPageStateTransition];
|
|
265
|
+
[pager setValue:@3 forKey:@"destinationIndex"];
|
|
266
|
+
[pager preparePageForHorizontalTransitionAtIndex:3];
|
|
267
|
+
[pager updatePageStateTransitionProgress:1];
|
|
268
|
+
XCTAssertEqualWithAccuracy([[pager valueForKey:@"headerOffset"] doubleValue], 60, 0.01);
|
|
269
|
+
[pager setValue:@3 forKey:@"currentIndex"];
|
|
270
|
+
[pager attachScrollObserverForCurrentPageAfterTransition:YES];
|
|
271
|
+
XCTAssertEqualObjects(offsets[@"page-1"], @60);
|
|
272
|
+
XCTAssertEqualObjects(offsets[@"page-2"], @360);
|
|
273
|
+
XCTAssertEqualObjects(offsets[@"page-3"], @660);
|
|
274
|
+
for (NSInteger index = 1; index < 4; index++) {
|
|
275
|
+
UIViewController *page = [pager valueForKey:@"pageControllers"][index];
|
|
276
|
+
RNCNativeScrollerComponentView *scroller = page.view.subviews.firstObject;
|
|
277
|
+
XCTAssertEqualWithAccuracy(scroller.scrollView.contentOffset.y + scroller.scrollView.contentInset.top,
|
|
278
|
+
60, 0.01);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
- (void)testZeroLayoutPreservesPendingBodyRestore
|
|
283
|
+
{
|
|
284
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:1];
|
|
285
|
+
RNCNativeScrollerComponentView *scroller = [self addScrollerToPager:pager atIndex:0];
|
|
286
|
+
NSMutableDictionary *offsets = [pager valueForKey:@"pageOffsets"];
|
|
287
|
+
offsets[@"page-0"] = @660;
|
|
288
|
+
[pager setValue:@120 forKey:@"headerOffset"];
|
|
289
|
+
scroller.scrollView.contentSize = CGSizeMake(0, 0);
|
|
290
|
+
[pager attachScrollObserverForCurrentPage];
|
|
291
|
+
XCTAssertEqualObjects(offsets[@"page-0"], @660);
|
|
292
|
+
XCTAssertTrue([[pager valueForKey:@"pendingPageRestoreKeys"] containsObject:@"page-0"]);
|
|
293
|
+
// Releasing an observed but unmeasured identity must retain its pending B.
|
|
294
|
+
scroller.tag = 91001;
|
|
295
|
+
ShadowView removedView;
|
|
296
|
+
removedView.tag = (Tag)scroller.tag;
|
|
297
|
+
MountingTransaction removal(1, 1, {ShadowViewMutation::RemoveMutation(1, removedView, 0)}, {});
|
|
298
|
+
SurfaceTelemetry telemetry;
|
|
299
|
+
[pager mountingTransactionWillMount:removal withSurfaceTelemetry:telemetry];
|
|
300
|
+
XCTAssertEqualObjects(offsets[@"page-0"], @660);
|
|
301
|
+
[self finishMount:pager];
|
|
302
|
+
XCTAssertEqualObjects(offsets[@"page-0"], @660);
|
|
303
|
+
scroller.scrollView.contentSize = CGSizeMake(320, 1500);
|
|
304
|
+
XCTAssertEqualObjects(offsets[@"page-0"], @660);
|
|
305
|
+
XCTAssertFalse([[pager valueForKey:@"pendingPageRestoreKeys"] containsObject:@"page-0"]);
|
|
306
|
+
XCTAssertEqualWithAccuracy(scroller.scrollView.contentOffset.y + scroller.scrollView.contentInset.top, 780, 0.01);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
- (void)testUnresolvedReplacementDoesNotCommitTemporaryClamp
|
|
310
|
+
{
|
|
311
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:2];
|
|
312
|
+
RNCNativeScrollerComponentView *source = [self addScrollerToPager:pager atIndex:0];
|
|
313
|
+
NSMutableDictionary *offsets = [pager valueForKey:@"pageOffsets"];
|
|
314
|
+
offsets[@"page-0"] = @0;
|
|
315
|
+
offsets[@"page-1"] = @660;
|
|
316
|
+
[pager setValue:@120 forKey:@"headerOffset"];
|
|
317
|
+
[pager attachScrollObserverForCurrentPage];
|
|
318
|
+
[pager beginPageStateTransition];
|
|
319
|
+
[pager setValue:@1 forKey:@"destinationIndex"];
|
|
320
|
+
UICollectionView *replacement = [[UICollectionView alloc]
|
|
321
|
+
initWithFrame:CGRectMake(0, 0, 320, 600)
|
|
322
|
+
collectionViewLayout:[UICollectionViewFlowLayout new]];
|
|
323
|
+
UIViewController *page = [pager valueForKey:@"pageControllers"][1];
|
|
324
|
+
[page.view addSubview:replacement];
|
|
325
|
+
id released = [NSClassFromString(@"RNCCollapsiblePagerReleasedScrollState") new];
|
|
326
|
+
XCTAssertNotNil(released);
|
|
327
|
+
[released setValue:source.scrollView forKey:@"scrollView"];
|
|
328
|
+
[released setValue:source forKey:@"componentView"];
|
|
329
|
+
[released setValue:@(source.tag) forKey:@"componentTag"];
|
|
330
|
+
[pager valueForKey:@"releasedPageScrollStates"][@"page-1"] = released;
|
|
331
|
+
[pager preparePageForHorizontalTransitionAtIndex:1];
|
|
332
|
+
[pager setValue:@1 forKey:@"currentIndex"];
|
|
333
|
+
[pager finishPageStateTransition];
|
|
334
|
+
XCTAssertEqualObjects(offsets[@"page-1"], @660);
|
|
335
|
+
XCTAssertTrue([[pager valueForKey:@"pendingPageRestoreKeys"] containsObject:@"page-1"]);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
- (void)testRenamedPageDoesNotResurrectPrunedObserverCache
|
|
339
|
+
{
|
|
340
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:1];
|
|
341
|
+
RNCNativeScrollerComponentView *scroller = [self addScrollerToPager:pager atIndex:0];
|
|
342
|
+
NSMutableDictionary *offsets = [pager valueForKey:@"pageOffsets"];
|
|
343
|
+
offsets[@"page-0"] = @60;
|
|
344
|
+
[pager attachScrollObserverForCurrentPage];
|
|
345
|
+
[pager setValue:@[@"replacement-key"] forKey:@"pageKeys"];
|
|
346
|
+
[offsets removeObjectForKey:@"page-0"];
|
|
347
|
+
scroller.scrollView.contentOffset = CGPointMake(0, 200);
|
|
348
|
+
[pager detachScrollObserver];
|
|
349
|
+
XCTAssertNil(offsets[@"page-0"]);
|
|
350
|
+
XCTAssertNil(offsets[@"replacement-key"]);
|
|
351
|
+
}
|
|
352
|
+
|
|
21
353
|
- (RNCCollapsiblePagerViewComponentView *)pagerWithPageCount:(NSInteger)count
|
|
22
354
|
{
|
|
23
355
|
RNCCollapsiblePagerViewComponentView *pager =
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#import <XCTest/XCTest.h>
|
|
2
2
|
#import <React/RCTViewComponentView.h>
|
|
3
3
|
#import "RNCCollapsiblePagerViewComponentView.h"
|
|
4
|
+
#import "RNCNativeScrollerComponentView.h"
|
|
4
5
|
|
|
5
6
|
@interface RNCCollapsiblePagerViewComponentView (RefreshControlTesting)
|
|
6
7
|
- (void)updateRefreshControlForScrollView:(UIScrollView *)scrollView;
|
|
@@ -8,6 +9,10 @@
|
|
|
8
9
|
- (void)attachScrollObserverForCurrentPage;
|
|
9
10
|
@end
|
|
10
11
|
|
|
12
|
+
@interface RNCNativeScrollerComponentView (RefreshControlScrollCommandTesting)
|
|
13
|
+
- (void)scrollTo:(double)x y:(double)y animated:(BOOL)animated;
|
|
14
|
+
@end
|
|
15
|
+
|
|
11
16
|
@interface RNCCollapsiblePagerRefreshControlTests : XCTestCase
|
|
12
17
|
@end
|
|
13
18
|
|
|
@@ -184,14 +189,18 @@
|
|
|
184
189
|
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithHeader:64];
|
|
185
190
|
[pager mountChildComponentView:[RCTViewComponentView new] index:0];
|
|
186
191
|
[pager mountChildComponentView:[RCTViewComponentView new] index:1];
|
|
187
|
-
|
|
192
|
+
RNCNativeScrollerComponentView *scroller = [RNCNativeScrollerComponentView new];
|
|
193
|
+
scroller.frame = CGRectMake(0, 0, 320, 600);
|
|
194
|
+
scroller.scrollView.frame = scroller.bounds;
|
|
195
|
+
UIScrollView *scroll = scroller.scrollView;
|
|
188
196
|
scroll.refreshControl = [UIRefreshControl new];
|
|
189
197
|
scroll.contentSize = CGSizeMake(320, 2000);
|
|
190
198
|
scroll.contentInset = UIEdgeInsetsMake(232, 0, 0, 0);
|
|
191
199
|
UIView *content = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 2000)];
|
|
192
200
|
[scroll addSubview:content];
|
|
193
201
|
UIViewController *page = [UIViewController new];
|
|
194
|
-
[page.view addSubview:
|
|
202
|
+
[page.view addSubview:scroller];
|
|
203
|
+
[pager addSubview:page.view];
|
|
195
204
|
NSMutableArray<UIViewController *> *pages = [pager valueForKey:@"pageControllers"];
|
|
196
205
|
[pages addObject:page];
|
|
197
206
|
[pager attachScrollObserverForCurrentPage];
|
|
@@ -204,7 +213,7 @@
|
|
|
204
213
|
XCTAssertEqualWithAccuracy(scroll.contentInset.top, 340, 0.001);
|
|
205
214
|
XCTAssertEqualWithAccuracy(headerHost.transform.ty, 0, 0.001);
|
|
206
215
|
|
|
207
|
-
|
|
216
|
+
[scroller scrollTo:0 y:500 animated:NO];
|
|
208
217
|
CGFloat baselineStickyTranslation = stickyHeader.transform.ty;
|
|
209
218
|
CGFloat baselineTabBarTranslation = tabBar.transform.ty;
|
|
210
219
|
XCTAssertEqualWithAccuracy(baselineStickyTranslation, 776, 0.001);
|
|
@@ -217,11 +226,11 @@
|
|
|
217
226
|
XCTAssertEqualWithAccuracy(tabBar.transform.ty, baselineTabBarTranslation, 0.001);
|
|
218
227
|
XCTAssertEqualWithAccuracy(CGRectGetMaxY(headerHost.frame), CGRectGetMinY(content.frame), 0.001);
|
|
219
228
|
|
|
220
|
-
// Exercise
|
|
229
|
+
// Exercise accepted native commands while the temporary refresh inset is
|
|
221
230
|
// present, including direction reversals and the sticky/unpinned boundary.
|
|
222
231
|
// The pre-fix formula adds the extra 60 points on these scroll updates.
|
|
223
232
|
for (NSNumber *nextOffset in @[@620, @450, @-300, @-340, @500]) {
|
|
224
|
-
|
|
233
|
+
[scroller scrollTo:0 y:nextOffset.doubleValue animated:NO];
|
|
225
234
|
CGFloat expectedTranslation = MAX(0, nextOffset.doubleValue + 340 - 64);
|
|
226
235
|
XCTAssertEqualWithAccuracy(headerHost.transform.ty, 0, 0.001);
|
|
227
236
|
XCTAssertEqualWithAccuracy(stickyHeader.transform.ty, expectedTranslation, 0.001);
|
|
@@ -71,6 +71,12 @@ export class CollapsiblePagerView extends React.PureComponent {
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
};
|
|
74
|
+
scrollToPageTop = options => {
|
|
75
|
+
if (this.nativeRef) CollapsiblePagerViewNativeCommands.scrollToTop(this.nativeRef, 0, options?.animated ?? true);
|
|
76
|
+
};
|
|
77
|
+
scrollToStickyTop = options => {
|
|
78
|
+
if (this.nativeRef) CollapsiblePagerViewNativeCommands.scrollToTop(this.nativeRef, 1, options?.animated ?? true);
|
|
79
|
+
};
|
|
74
80
|
setScrollEnabled = scrollEnabled => {
|
|
75
81
|
if (this.nativeRef) {
|
|
76
82
|
CollapsiblePagerViewNativeCommands.setScrollEnabledImperatively(this.nativeRef, scrollEnabled);
|