@onekeyfe/react-native-pager-view 6.7.0-alpha.430 → 6.7.0-alpha.435
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/docs/SPEC.md +40 -0
- package/ios/RNCCollapsiblePagerViewComponentView.mm +410 -19
- package/ios/tests/RNCCollapsiblePagerHitTestTests.mm +756 -0
- package/lib/module/CollapsiblePagerViewNativeComponent.ts +41 -46
- package/lib/module/NativeScroller.web.js +3 -0
- package/lib/module/NativeScrollerNativeComponent.ts +43 -46
- package/lib/module/PagerViewNativeComponent.ts +20 -25
- package/lib/typescript/src/CollapsiblePagerView.d.ts +1 -1
- package/lib/typescript/src/CollapsiblePagerView.web.d.ts +1 -1
- package/lib/typescript/src/CollapsiblePagerViewNativeComponent.d.ts +41 -42
- package/lib/typescript/src/NativeScroller.d.ts +5 -3
- package/lib/typescript/src/NativeScroller.web.d.ts +1 -1
- package/lib/typescript/src/NativeScrollerNativeComponent.d.ts +47 -48
- package/lib/typescript/src/PagerView.d.ts +1 -1
- package/lib/typescript/src/PagerView.web.d.ts +1 -1
- package/lib/typescript/src/PagerViewNativeComponent.d.ts +20 -21
- package/lib/typescript/src/WebCollapsiblePagerPage.d.ts +1 -1
- package/lib/typescript/src/usePagerView.d.ts +2 -2
- package/lib/typescript/src/utils.d.ts +2 -2
- package/package.json +5 -5
- package/src/CollapsiblePagerViewNativeComponent.ts +41 -46
- package/src/NativeScroller.tsx +14 -3
- package/src/NativeScroller.web.tsx +15 -1
- package/src/NativeScrollerNativeComponent.ts +43 -46
- package/src/PagerViewNativeComponent.ts +20 -25
|
@@ -12,6 +12,19 @@ using namespace facebook::react;
|
|
|
12
12
|
- (void)updateTopScrollEdgeForScrollView:(UIScrollView *)scrollView;
|
|
13
13
|
- (void)updateScrollEdgeContentMasks;
|
|
14
14
|
- (void)layoutSharedHeaders;
|
|
15
|
+
- (void)adoptCurrentScrollEdgeForProgrammaticTransition:(BOOL)allowExternalOverlay;
|
|
16
|
+
- (BOOL)resolveExternalTopOverlay;
|
|
17
|
+
- (void)beginProgrammaticScrollEdgesToIndex:(NSInteger)index allowExternalOverlay:(BOOL)allowExternalOverlay;
|
|
18
|
+
- (void)outerPagerDidUpdateOverlayOwner:(BOOL)active transitioning:(BOOL)transitioning;
|
|
19
|
+
- (void)restorePinnedStickyHeader;
|
|
20
|
+
- (void)finishProgrammaticScrollEdges;
|
|
21
|
+
- (void)clearProgrammaticScrollEdges;
|
|
22
|
+
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView;
|
|
23
|
+
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;
|
|
24
|
+
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;
|
|
25
|
+
- (void)goTo:(NSInteger)index animated:(BOOL)animated;
|
|
26
|
+
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView;
|
|
27
|
+
- (void)emitPageSelected:(NSInteger)position;
|
|
15
28
|
@end
|
|
16
29
|
|
|
17
30
|
@interface UIView (NativeTabReservationTesting)
|
|
@@ -33,6 +46,19 @@ using namespace facebook::react;
|
|
|
33
46
|
- (void)setLeftToRight:(BOOL)leftToRight;
|
|
34
47
|
@end
|
|
35
48
|
|
|
49
|
+
@interface RNCCollapsiblePagerResizeSelectionSpy : RNCCollapsiblePagerViewComponentView
|
|
50
|
+
@property (nonatomic, strong) NSMutableArray<NSNumber *> *selectedIndexes;
|
|
51
|
+
@end
|
|
52
|
+
|
|
53
|
+
@implementation RNCCollapsiblePagerResizeSelectionSpy
|
|
54
|
+
- (void)emitPageSelected:(NSInteger)position
|
|
55
|
+
{
|
|
56
|
+
if (_selectedIndexes == nil) _selectedIndexes = [NSMutableArray new];
|
|
57
|
+
[_selectedIndexes addObject:@(position)];
|
|
58
|
+
[super emitPageSelected:position];
|
|
59
|
+
}
|
|
60
|
+
@end
|
|
61
|
+
|
|
36
62
|
@interface RNCCollapsiblePagerHitTestTests : XCTestCase
|
|
37
63
|
@property (nonatomic, strong) UIView *root;
|
|
38
64
|
@property (nonatomic, strong) UIButton *toolbar;
|
|
@@ -390,6 +416,736 @@ using namespace facebook::react;
|
|
|
390
416
|
}
|
|
391
417
|
}
|
|
392
418
|
|
|
419
|
+
- (UIScrollView *)preparePinnedLocalHardSource
|
|
420
|
+
{
|
|
421
|
+
UIWindow *window = [[UIWindow alloc] initWithFrame:self.root.bounds];
|
|
422
|
+
[window addSubview:self.root];
|
|
423
|
+
window.hidden = NO;
|
|
424
|
+
[self addTeardownBlock:^{
|
|
425
|
+
[self.pager prepareForRecycle];
|
|
426
|
+
window.hidden = YES;
|
|
427
|
+
}];
|
|
428
|
+
UIView *container = [self.pager valueForKey:@"containerView"];
|
|
429
|
+
container.frame = self.pager.bounds;
|
|
430
|
+
UIViewController *page = [UIViewController new];
|
|
431
|
+
page.view = [[UIView alloc] initWithFrame:container.bounds];
|
|
432
|
+
UIScrollView *source = [[UIScrollView alloc] initWithFrame:page.view.bounds];
|
|
433
|
+
source.contentSize = CGSizeMake(320, 1200);
|
|
434
|
+
source.topEdgeEffect.style = UIScrollEdgeEffectStyle.softStyle;
|
|
435
|
+
[page.view addSubview:source];
|
|
436
|
+
[container insertSubview:page.view atIndex:0];
|
|
437
|
+
[[self.pager valueForKey:@"pageControllers"] addObject:page];
|
|
438
|
+
[self.pager setValue:source forKey:@"observedScrollView"];
|
|
439
|
+
[self.pager setValue:source forKey:@"sharedHeaderScrollView"];
|
|
440
|
+
[self.pager setValue:@YES forKey:@"directNativePagerEnabled"];
|
|
441
|
+
[self.pager setValue:@YES forKey:@"topScrollEdgeEnabled"];
|
|
442
|
+
[self.pager setValue:@(static_cast<NSInteger>(RNCCollapsiblePagerViewTopScrollEdgeEffect::Hard))
|
|
443
|
+
forKey:@"topScrollEdgeEffect"];
|
|
444
|
+
UIView *overlayHost = [self.pager valueForKey:@"topOverlayHostView"];
|
|
445
|
+
overlayHost.frame = CGRectMake(0, 0, 320, 50);
|
|
446
|
+
overlayHost.hidden = NO;
|
|
447
|
+
UIView *overlay = [[UIView alloc] initWithFrame:overlayHost.bounds];
|
|
448
|
+
[overlayHost addSubview:overlay];
|
|
449
|
+
[self.pager setValue:overlay forKey:@"topOverlayView"];
|
|
450
|
+
[self.pager setValue:@50 forKey:@"topOverlayHeight"];
|
|
451
|
+
[self.pager setValue:@100 forKey:@"headerHeight"];
|
|
452
|
+
[self.pager setValue:@44 forKey:@"stickyHeaderHeight"];
|
|
453
|
+
[self.pager setValue:@100 forKey:@"headerOffset"];
|
|
454
|
+
UIView *pinnedHost = [self.pager valueForKey:@"pinnedStickyHostView"];
|
|
455
|
+
pinnedHost.frame = CGRectMake(0, 50, 320, 44);
|
|
456
|
+
pinnedHost.hidden = NO;
|
|
457
|
+
[source addSubview:pinnedHost];
|
|
458
|
+
[pinnedHost addSubview:self.sticky];
|
|
459
|
+
[self.pager setValue:self.sticky forKey:@"stickyHeaderView"];
|
|
460
|
+
[self.pager setValue:@YES forKey:@"stickyHeaderIsPinned"];
|
|
461
|
+
[self.pager updateTopScrollEdgeForScrollView:source];
|
|
462
|
+
return source;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
- (UIScrollView *)prepareDirectDragWithSource:(UIScrollView **)sourceOut
|
|
466
|
+
{
|
|
467
|
+
UIScrollView *source = [self preparePinnedLocalHardSource];
|
|
468
|
+
UIView *container = [self.pager valueForKey:@"containerView"];
|
|
469
|
+
UIScrollView *horizontal = [[UIScrollView alloc] initWithFrame:container.bounds];
|
|
470
|
+
horizontal.contentSize = CGSizeMake(960, CGRectGetHeight(container.bounds));
|
|
471
|
+
horizontal.pagingEnabled = YES;
|
|
472
|
+
horizontal.delegate = (id<UIScrollViewDelegate>)self.pager;
|
|
473
|
+
[container insertSubview:horizontal atIndex:0];
|
|
474
|
+
NSMutableArray<UIViewController *> *pages = [self.pager valueForKey:@"pageControllers"];
|
|
475
|
+
UIViewController *sourcePage = pages.firstObject;
|
|
476
|
+
for (NSInteger index = 0; index < 3; index++) {
|
|
477
|
+
UIViewController *page = index == 1 ? sourcePage : [UIViewController new];
|
|
478
|
+
if (index != 1) {
|
|
479
|
+
page.view = [[UIView alloc] initWithFrame:container.bounds];
|
|
480
|
+
UIScrollView *list = [[UIScrollView alloc] initWithFrame:page.view.bounds];
|
|
481
|
+
list.contentSize = CGSizeMake(320, 1200);
|
|
482
|
+
list.topEdgeEffect.style = UIScrollEdgeEffectStyle.softStyle;
|
|
483
|
+
[page.view addSubview:list];
|
|
484
|
+
}
|
|
485
|
+
page.view.frame = CGRectMake(index * 320, 0, 320, CGRectGetHeight(container.bounds));
|
|
486
|
+
[horizontal addSubview:page.view];
|
|
487
|
+
if (index == 0) [pages insertObject:page atIndex:0];
|
|
488
|
+
if (index == 2) [pages addObject:page];
|
|
489
|
+
}
|
|
490
|
+
horizontal.contentOffset = CGPointMake(320, 0);
|
|
491
|
+
[self.pager setValue:horizontal forKey:@"pagerScrollView"];
|
|
492
|
+
[self.pager setValue:@1 forKey:@"currentIndex"];
|
|
493
|
+
[self.pager setValue:@1 forKey:@"destinationIndex"];
|
|
494
|
+
[self.pager setValue:@"ltr" forKey:@"layoutDirection"];
|
|
495
|
+
[self.pager setValue:@YES forKey:@"hasAppliedInitialPage"];
|
|
496
|
+
if (sourceOut != nil) *sourceOut = source;
|
|
497
|
+
return horizontal;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
- (UIScrollView *)prepareExternalDirectDragWithSource:(UIScrollView **)sourceOut
|
|
501
|
+
overlay:(UIView **)overlayOut
|
|
502
|
+
{
|
|
503
|
+
UIScrollView *source = nil;
|
|
504
|
+
UIScrollView *horizontal = [self prepareDirectDragWithSource:&source];
|
|
505
|
+
[self.pager updateTopScrollEdgeForScrollView:nil];
|
|
506
|
+
UIView *local = [self.pager valueForKey:@"topOverlayView"];
|
|
507
|
+
[local removeFromSuperview];
|
|
508
|
+
[self.pager setValue:nil forKey:@"topOverlayView"];
|
|
509
|
+
[self.pager setValue:@0 forKey:@"topOverlayHeight"];
|
|
510
|
+
UIView *overlay = [[UIView alloc] initWithFrame:CGRectMake(0, 100, 320, 50)];
|
|
511
|
+
overlay.tag = 8675309;
|
|
512
|
+
[self.root addSubview:overlay];
|
|
513
|
+
auto props = std::make_shared<RNCCollapsiblePagerViewProps>();
|
|
514
|
+
props->nativeSmoothHeaderScrollEnabled = true;
|
|
515
|
+
props->topScrollEdgeEffect = RNCCollapsiblePagerViewTopScrollEdgeEffect::Hard;
|
|
516
|
+
props->externalTopOverlayTag = overlay.tag;
|
|
517
|
+
[self.pager updateProps:props oldProps:nullptr];
|
|
518
|
+
[self.pager resolveExternalTopOverlay];
|
|
519
|
+
XCTAssertEqual([self.pager valueForKey:@"externalTopOverlayView"], overlay);
|
|
520
|
+
XCTAssertEqualWithAccuracy([[self.pager valueForKey:@"externalTopOverlayHeight"] doubleValue], 50, 0.001);
|
|
521
|
+
XCTAssertEqual(overlay.window, self.pager.window);
|
|
522
|
+
XCTAssertFalse([overlay isDescendantOfView:self.pager]);
|
|
523
|
+
UIView *proxy = [self.pager valueForKey:@"topOverlayHostView"];
|
|
524
|
+
XCTAssertEqual(proxy.window, self.pager.window);
|
|
525
|
+
XCTAssertFalse(proxy.hidden);
|
|
526
|
+
XCTAssertFalse(proxy.userInteractionEnabled);
|
|
527
|
+
[self.pager updateTopScrollEdgeForScrollView:source];
|
|
528
|
+
XCTAssertEqual([[self.pager valueForKey:@"pageControllers"] count], 3);
|
|
529
|
+
XCTAssertEqual([[self.pager valueForKey:@"currentIndex"] integerValue], 1);
|
|
530
|
+
XCTAssertEqualWithAccuracy(horizontal.contentOffset.x, 320, 0.001);
|
|
531
|
+
XCTAssertTrue([[self.pager valueForKey:@"hasAppliedInitialPage"] boolValue]);
|
|
532
|
+
XCTAssertTrue([[self.pager valueForKey:@"directNativePagerEnabled"] boolValue]);
|
|
533
|
+
XCTAssertEqualWithAccuracy([[self.pager valueForKey:@"headerOffset"] doubleValue], 100, 0.001);
|
|
534
|
+
XCTAssertTrue([[self.pager valueForKey:@"stickyHeaderIsPinned"] boolValue]);
|
|
535
|
+
XCTAssertNotNil([self.pager valueForKey:@"stickyEdgeInteraction"]);
|
|
536
|
+
if (sourceOut != nil) *sourceOut = source;
|
|
537
|
+
if (overlayOut != nil) *overlayOut = overlay;
|
|
538
|
+
return horizontal;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
- (UIScrollView *)prepareNativeResizeCommandPager
|
|
542
|
+
{
|
|
543
|
+
[self.pager removeFromSuperview];
|
|
544
|
+
self.pager = [RNCCollapsiblePagerResizeSelectionSpy new];
|
|
545
|
+
[self setPointerEvents:PointerEventsMode::Auto];
|
|
546
|
+
self.pager.frame = CGRectMake(0, 100, 320, 500);
|
|
547
|
+
[self.root addSubview:self.pager];
|
|
548
|
+
UIScrollView *horizontal = [self prepareDirectDragWithSource:NULL];
|
|
549
|
+
XCTAssertEqual([[self.pager valueForKey:@"currentIndex"] integerValue], 1);
|
|
550
|
+
XCTAssertEqualWithAccuracy(CGRectGetWidth(horizontal.bounds), 320, 0.001);
|
|
551
|
+
XCTAssertEqualWithAccuracy(horizontal.contentOffset.x, 320, 0.001);
|
|
552
|
+
XCTAssertTrue(horizontal.scrollEnabled);
|
|
553
|
+
return horizontal;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
- (void)waitForNativeResizeCommandsToSettle
|
|
557
|
+
{
|
|
558
|
+
NSPredicate *settled = [NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary *bindings) {
|
|
559
|
+
return ![[self.pager valueForKey:@"transitioning"] boolValue] &&
|
|
560
|
+
[self.pager valueForKey:@"pendingNativePagerScrollCompletion"] == nil &&
|
|
561
|
+
[[self.pager valueForKey:@"pendingGoToIndex"] integerValue] == -1;
|
|
562
|
+
}];
|
|
563
|
+
XCTNSPredicateExpectation *expectation = [[XCTNSPredicateExpectation alloc]
|
|
564
|
+
initWithPredicate:settled object:self.pager];
|
|
565
|
+
[self waitForExpectations:@[expectation] timeout:3];
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
- (void)testNativeAnimatedCommandResizeRebasesAndDrainsQueueIgnoringLateEnd
|
|
569
|
+
{
|
|
570
|
+
if (@available(iOS 27.0, *)) {
|
|
571
|
+
UIScrollView *horizontal = [self prepareNativeResizeCommandPager];
|
|
572
|
+
RNCCollapsiblePagerResizeSelectionSpy *spy = (id)self.pager;
|
|
573
|
+
[self.pager goTo:2 animated:YES];
|
|
574
|
+
id originalPending = [self.pager valueForKey:@"pendingNativePagerScrollCompletion"];
|
|
575
|
+
XCTAssertNotNil(originalPending);
|
|
576
|
+
XCTAssertFalse(horizontal.scrollEnabled);
|
|
577
|
+
XCTAssertEqualWithAccuracy([[self.pager valueForKey:@"pendingNativePagerScrollTarget"] doubleValue], 640, 0.001);
|
|
578
|
+
self.pager.frame = CGRectMake(0, 100, 320, 480);
|
|
579
|
+
[self.pager layoutSubviews];
|
|
580
|
+
XCTAssertEqual([self.pager valueForKey:@"pendingNativePagerScrollCompletion"], originalPending);
|
|
581
|
+
XCTAssertEqual(spy.selectedIndexes.count, 0);
|
|
582
|
+
[self.pager goTo:0 animated:YES];
|
|
583
|
+
XCTAssertEqual([[self.pager valueForKey:@"pendingGoToIndex"] integerValue], 0);
|
|
584
|
+
self.pager.frame = CGRectMake(0, 100, 320.4, 480);
|
|
585
|
+
[self.pager layoutSubviews];
|
|
586
|
+
XCTAssertNil([self.pager valueForKey:@"pendingNativePagerScrollCompletion"]);
|
|
587
|
+
XCTAssertEqualWithAccuracy(horizontal.contentOffset.x, 640.8, 1.0 / horizontal.window.screen.scale);
|
|
588
|
+
XCTAssertGreaterThan(horizontal.contentOffset.x, 640.5);
|
|
589
|
+
XCTAssertEqual([[self.pager valueForKey:@"currentIndex"] integerValue], 2);
|
|
590
|
+
XCTAssertTrue(horizontal.scrollEnabled);
|
|
591
|
+
XCTAssertEqualObjects(spy.selectedIndexes, (@[@2]));
|
|
592
|
+
[self.pager scrollViewDidEndScrollingAnimation:horizontal];
|
|
593
|
+
XCTAssertEqualObjects(spy.selectedIndexes, (@[@2]));
|
|
594
|
+
XCTestExpectation *queued = [self expectationWithDescription:@"queued native command starts"];
|
|
595
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
596
|
+
id pending = [self.pager valueForKey:@"pendingNativePagerScrollCompletion"];
|
|
597
|
+
XCTAssertNotNil(pending);
|
|
598
|
+
XCTAssertNotEqual(pending, originalPending);
|
|
599
|
+
XCTAssertEqualWithAccuracy([[self.pager valueForKey:@"pendingNativePagerScrollTarget"] doubleValue], 0, 0.001);
|
|
600
|
+
XCTAssertGreaterThan(fabs(horizontal.contentOffset.x), 0.5);
|
|
601
|
+
[self.pager scrollViewDidEndScrollingAnimation:horizontal];
|
|
602
|
+
XCTAssertEqual([self.pager valueForKey:@"pendingNativePagerScrollCompletion"], pending);
|
|
603
|
+
XCTAssertEqualObjects(spy.selectedIndexes, (@[@2]));
|
|
604
|
+
[queued fulfill];
|
|
605
|
+
});
|
|
606
|
+
[self waitForExpectations:@[queued] timeout:2];
|
|
607
|
+
// This hostless fixture does not drive the natural scroll animation.
|
|
608
|
+
// Exercise the controlled UIKit end boundary after checking stale-end rejection.
|
|
609
|
+
XCTAssertNotNil([self.pager valueForKey:@"pendingNativePagerScrollCompletion"]);
|
|
610
|
+
[horizontal setContentOffset:CGPointZero animated:NO];
|
|
611
|
+
[self.pager scrollViewDidEndScrollingAnimation:horizontal];
|
|
612
|
+
[self waitForNativeResizeCommandsToSettle];
|
|
613
|
+
XCTAssertEqualWithAccuracy(horizontal.contentOffset.x, 0, 0.001);
|
|
614
|
+
XCTAssertEqual([[self.pager valueForKey:@"currentIndex"] integerValue], 0);
|
|
615
|
+
XCTAssertTrue(horizontal.scrollEnabled);
|
|
616
|
+
XCTAssertEqualObjects(spy.selectedIndexes, (@[@2, @0]));
|
|
617
|
+
XCTAssertEqual([[self.pager valueForKey:@"programmaticScrollEdgeOwners"] count], 0);
|
|
618
|
+
} else {
|
|
619
|
+
XCTSkip(@"Native local Hard animation requires iOS 27 or newer");
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
- (void)testNativeAnimatedCommandResizeRebasesNonzeroRTLTarget
|
|
624
|
+
{
|
|
625
|
+
if (@available(iOS 27.0, *)) {
|
|
626
|
+
UIScrollView *horizontal = [self prepareNativeResizeCommandPager];
|
|
627
|
+
RNCCollapsiblePagerResizeSelectionSpy *spy = (id)self.pager;
|
|
628
|
+
[self.pager setValue:@"rtl" forKey:@"layoutDirection"];
|
|
629
|
+
[self.pager goTo:0 animated:YES];
|
|
630
|
+
XCTAssertNotNil([self.pager valueForKey:@"pendingNativePagerScrollCompletion"]);
|
|
631
|
+
XCTAssertEqualWithAccuracy([[self.pager valueForKey:@"pendingNativePagerScrollTarget"] doubleValue], 640, 0.001);
|
|
632
|
+
self.pager.frame = CGRectMake(0, 100, 400, 500);
|
|
633
|
+
[self.pager layoutSubviews];
|
|
634
|
+
XCTAssertNil([self.pager valueForKey:@"pendingNativePagerScrollCompletion"]);
|
|
635
|
+
XCTAssertEqualWithAccuracy(horizontal.contentOffset.x, 800, 0.001);
|
|
636
|
+
XCTAssertEqual([[self.pager valueForKey:@"currentIndex"] integerValue], 0);
|
|
637
|
+
[self.pager scrollViewDidEndScrollingAnimation:horizontal];
|
|
638
|
+
[self waitForNativeResizeCommandsToSettle];
|
|
639
|
+
XCTAssertEqualObjects(spy.selectedIndexes, (@[@0]));
|
|
640
|
+
XCTAssertTrue(horizontal.scrollEnabled);
|
|
641
|
+
XCTAssertEqual([[self.pager valueForKey:@"programmaticScrollEdgeOwners"] count], 0);
|
|
642
|
+
} else {
|
|
643
|
+
XCTSkip(@"Native local Hard animation requires iOS 27 or newer");
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
- (void)testNativeAnimatedCommandZeroWidthCompletesQueueAndRealignsOnRestore
|
|
648
|
+
{
|
|
649
|
+
if (@available(iOS 27.0, *)) {
|
|
650
|
+
UIScrollView *horizontal = [self prepareNativeResizeCommandPager];
|
|
651
|
+
RNCCollapsiblePagerResizeSelectionSpy *spy = (id)self.pager;
|
|
652
|
+
[self.pager goTo:0 animated:YES];
|
|
653
|
+
XCTAssertNotNil([self.pager valueForKey:@"pendingNativePagerScrollCompletion"]);
|
|
654
|
+
[self.pager goTo:2 animated:YES];
|
|
655
|
+
self.pager.frame = CGRectMake(0, 100, 0, 500);
|
|
656
|
+
[self.pager layoutSubviews];
|
|
657
|
+
XCTAssertNil([self.pager valueForKey:@"pendingNativePagerScrollCompletion"]);
|
|
658
|
+
XCTAssertEqual([[self.pager valueForKey:@"currentIndex"] integerValue], 0);
|
|
659
|
+
XCTAssertTrue(horizontal.scrollEnabled);
|
|
660
|
+
[self.pager scrollViewDidEndScrollingAnimation:horizontal];
|
|
661
|
+
[self waitForNativeResizeCommandsToSettle];
|
|
662
|
+
XCTAssertEqual([[self.pager valueForKey:@"currentIndex"] integerValue], 2);
|
|
663
|
+
XCTAssertTrue(horizontal.scrollEnabled);
|
|
664
|
+
XCTAssertEqualObjects(spy.selectedIndexes, (@[@0, @2]));
|
|
665
|
+
self.pager.frame = CGRectMake(0, 100, 400, 500);
|
|
666
|
+
[self.pager layoutSubviews];
|
|
667
|
+
XCTAssertEqualWithAccuracy(horizontal.contentOffset.x, 800, 0.001);
|
|
668
|
+
[self.pager scrollViewDidEndScrollingAnimation:horizontal];
|
|
669
|
+
XCTAssertEqualObjects(spy.selectedIndexes, (@[@0, @2]));
|
|
670
|
+
XCTAssertNil([self.pager valueForKey:@"pendingNativePagerScrollCompletion"]);
|
|
671
|
+
XCTAssertFalse([[self.pager valueForKey:@"transitioning"] boolValue]);
|
|
672
|
+
XCTAssertEqual([[self.pager valueForKey:@"programmaticScrollEdgeOwners"] count], 0);
|
|
673
|
+
} else {
|
|
674
|
+
XCTSkip(@"Native local Hard animation requires iOS 27 or newer");
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
- (void)testExternalDirectDragPreservesProxyIdentityAndSettles
|
|
679
|
+
{
|
|
680
|
+
if (@available(iOS 27.0, *)) {
|
|
681
|
+
UIScrollView *source = nil;
|
|
682
|
+
UIView *overlay = nil;
|
|
683
|
+
UIScrollView *horizontal = [self prepareExternalDirectDragWithSource:&source overlay:&overlay];
|
|
684
|
+
UIView *proxy = [self.pager valueForKey:@"topOverlayHostView"];
|
|
685
|
+
UIView *pinned = [self.pager valueForKey:@"pinnedStickyHostView"];
|
|
686
|
+
UIScrollEdgeElementContainerInteraction *top = [self.pager valueForKey:@"topEdgeInteraction"];
|
|
687
|
+
UIScrollEdgeElementContainerInteraction *sticky = [self.pager valueForKey:@"stickyEdgeInteraction"];
|
|
688
|
+
UIView *marker = [self.pager valueForKey:@"topEdgeMarker"];
|
|
689
|
+
UIView *stickyMarker = [self.pager valueForKey:@"stickyEdgeMarker"];
|
|
690
|
+
NSArray<UIViewController *> *pages = [self.pager valueForKey:@"pageControllers"];
|
|
691
|
+
UIScrollView *target = (UIScrollView *)pages[2].view.subviews.firstObject;
|
|
692
|
+
XCTAssertNotNil(top);
|
|
693
|
+
XCTAssertNotNil(sticky);
|
|
694
|
+
XCTAssertTrue([proxy.interactions containsObject:top]);
|
|
695
|
+
XCTAssertEqual(overlay.interactions.count, 0);
|
|
696
|
+
[self.pager adoptCurrentScrollEdgeForProgrammaticTransition:NO];
|
|
697
|
+
[self.pager beginProgrammaticScrollEdgesToIndex:2 allowExternalOverlay:NO];
|
|
698
|
+
XCTAssertEqual([[self.pager valueForKey:@"programmaticScrollEdgeOwners"] count], 0);
|
|
699
|
+
XCTAssertEqual([self.pager valueForKey:@"topEdgeInteraction"], top);
|
|
700
|
+
|
|
701
|
+
[self.pager scrollViewWillBeginDragging:horizontal];
|
|
702
|
+
NSArray *owners = [self.pager valueForKey:@"programmaticScrollEdgeOwners"];
|
|
703
|
+
XCTAssertEqual(owners.count, 3);
|
|
704
|
+
id sourceOwner = owners.firstObject;
|
|
705
|
+
XCTAssertEqual([sourceOwner valueForKey:@"topInteraction"], top);
|
|
706
|
+
XCTAssertEqual([sourceOwner valueForKey:@"stickyInteraction"], sticky);
|
|
707
|
+
XCTAssertEqual([sourceOwner valueForKey:@"topMarker"], marker);
|
|
708
|
+
XCTAssertEqual([sourceOwner valueForKey:@"topHost"], proxy);
|
|
709
|
+
XCTAssertEqual([sourceOwner valueForKey:@"originalStyle"], UIScrollEdgeEffectStyle.softStyle);
|
|
710
|
+
XCTAssertTrue([[sourceOwner valueForKey:@"borrowsPinnedStickyHost"] boolValue]);
|
|
711
|
+
XCTAssertFalse([[sourceOwner valueForKey:@"ownsTransitionHosts"] boolValue]);
|
|
712
|
+
XCTAssertEqual(top.scrollView, source);
|
|
713
|
+
XCTAssertEqual(sticky.scrollView, source);
|
|
714
|
+
XCTAssertEqual(marker.superview, proxy);
|
|
715
|
+
XCTAssertEqual(pinned.superview, source);
|
|
716
|
+
XCTAssertFalse([self.pager resolveExternalTopOverlay]);
|
|
717
|
+
[self.pager updateTopScrollEdgeForScrollView:source];
|
|
718
|
+
XCTAssertEqual([[self.pager valueForKey:@"programmaticScrollEdgeOwners"] count], 3);
|
|
719
|
+
XCTAssertEqual(overlay.superview, self.root);
|
|
720
|
+
XCTAssertEqual(overlay.interactions.count, 0);
|
|
721
|
+
|
|
722
|
+
NSMutableArray *incomingOwners = [NSMutableArray new];
|
|
723
|
+
NSMutableArray<UIView *> *ownedHosts = [NSMutableArray new];
|
|
724
|
+
NSMutableArray<UIView *> *ownedMarkers = [NSMutableArray new];
|
|
725
|
+
NSMutableArray<UIScrollEdgeElementContainerInteraction *> *ownedEdges = [NSMutableArray new];
|
|
726
|
+
for (id owner in owners) {
|
|
727
|
+
if (owner == sourceOwner) continue;
|
|
728
|
+
[incomingOwners addObject:owner];
|
|
729
|
+
XCTAssertTrue([[owner valueForKey:@"ownsTransitionHosts"] boolValue]);
|
|
730
|
+
UIView *page = [owner valueForKey:@"page"];
|
|
731
|
+
UIScrollView *list = [owner valueForKey:@"scrollView"];
|
|
732
|
+
UIView *ownedTop = [owner valueForKey:@"ownedTopHost"];
|
|
733
|
+
UIView *ownedSticky = [owner valueForKey:@"ownedStickyHost"];
|
|
734
|
+
XCTAssertNotNil(ownedTop);
|
|
735
|
+
XCTAssertNotNil(ownedSticky);
|
|
736
|
+
XCTAssertEqual([owner valueForKey:@"topHost"], ownedTop);
|
|
737
|
+
XCTAssertEqual([owner valueForKey:@"stickyHost"], ownedSticky);
|
|
738
|
+
XCTAssertNotEqual(ownedTop, ownedSticky);
|
|
739
|
+
for (NSString *edgeName in @[@"top", @"sticky"]) {
|
|
740
|
+
UIView *host = [owner valueForKey:[edgeName stringByAppendingString:@"Host"]];
|
|
741
|
+
UIView *edgeMarker = [owner valueForKey:[edgeName stringByAppendingString:@"Marker"]];
|
|
742
|
+
UIScrollEdgeElementContainerInteraction *edge =
|
|
743
|
+
[owner valueForKey:[edgeName stringByAppendingString:@"Interaction"]];
|
|
744
|
+
XCTAssertFalse([ownedHosts containsObject:host]);
|
|
745
|
+
[ownedHosts addObject:host];
|
|
746
|
+
[ownedMarkers addObject:edgeMarker];
|
|
747
|
+
[ownedEdges addObject:edge];
|
|
748
|
+
XCTAssertEqual(host.superview, page);
|
|
749
|
+
XCTAssertNotEqual(host, proxy);
|
|
750
|
+
XCTAssertNotEqual(host, self.sticky);
|
|
751
|
+
XCTAssertNotEqual(host, overlay);
|
|
752
|
+
XCTAssertFalse(host.userInteractionEnabled);
|
|
753
|
+
XCTAssertTrue(host.accessibilityElementsHidden);
|
|
754
|
+
if (host.backgroundColor != nil) {
|
|
755
|
+
XCTAssertEqualWithAccuracy(CGColorGetAlpha(host.backgroundColor.CGColor), 0, 0.001);
|
|
756
|
+
}
|
|
757
|
+
XCTAssertEqual(edge.view, host);
|
|
758
|
+
XCTAssertEqual(edge.scrollView, list);
|
|
759
|
+
XCTAssertEqual(edgeMarker.superview, host);
|
|
760
|
+
XCTAssertEqualWithAccuracy(host.frame.origin.x,
|
|
761
|
+
CGRectGetMinX([list convertRect:list.bounds toView:page]), 0.001);
|
|
762
|
+
}
|
|
763
|
+
CGRect pageFrame = page.frame;
|
|
764
|
+
CGRect listFrame = list.frame;
|
|
765
|
+
CGRect topFrame = ownedTop.frame;
|
|
766
|
+
CGRect stickyFrame = ownedSticky.frame;
|
|
767
|
+
page.frame = CGRectMake(pageFrame.origin.x, pageFrame.origin.y, 400, pageFrame.size.height);
|
|
768
|
+
list.frame = CGRectMake(listFrame.origin.x, listFrame.origin.y, 400, listFrame.size.height);
|
|
769
|
+
XCTAssertEqualWithAccuracy(ownedTop.frame.size.width, 400, 0.001);
|
|
770
|
+
XCTAssertEqualWithAccuracy(ownedSticky.frame.size.width, 400, 0.001);
|
|
771
|
+
XCTAssertEqualWithAccuracy(((UIView *)[owner valueForKey:@"topMarker"]).frame.size.width, 400, 0.001);
|
|
772
|
+
XCTAssertEqualWithAccuracy(((UIView *)[owner valueForKey:@"stickyMarker"]).frame.size.width, 400, 0.001);
|
|
773
|
+
XCTAssertEqualWithAccuracy(ownedTop.frame.origin.y, topFrame.origin.y, 0.001);
|
|
774
|
+
XCTAssertEqualWithAccuracy(ownedSticky.frame.origin.y, stickyFrame.origin.y, 0.001);
|
|
775
|
+
XCTAssertEqualWithAccuracy(ownedTop.frame.size.height, topFrame.size.height, 0.001);
|
|
776
|
+
XCTAssertEqualWithAccuracy(ownedSticky.frame.size.height, stickyFrame.size.height, 0.001);
|
|
777
|
+
page.frame = pageFrame;
|
|
778
|
+
list.frame = listFrame;
|
|
779
|
+
}
|
|
780
|
+
XCTAssertEqual(incomingOwners.count, 2);
|
|
781
|
+
XCTAssertEqual(ownedHosts.count, 4);
|
|
782
|
+
horizontal.contentOffset = CGPointMake(480, 0);
|
|
783
|
+
for (id owner in incomingOwners) {
|
|
784
|
+
UIScrollView *list = [owner valueForKey:@"scrollView"];
|
|
785
|
+
CGRect viewport = [list convertRect:list.bounds toView:nil];
|
|
786
|
+
UIView *ownedTop = [owner valueForKey:@"ownedTopHost"];
|
|
787
|
+
UIView *ownedSticky = [owner valueForKey:@"ownedStickyHost"];
|
|
788
|
+
CGRect topWindow = [ownedTop convertRect:ownedTop.bounds toView:nil];
|
|
789
|
+
CGRect stickyWindow = [ownedSticky convertRect:ownedSticky.bounds toView:nil];
|
|
790
|
+
XCTAssertNotEqualWithAccuracy(viewport.origin.x, 0, 0.001);
|
|
791
|
+
XCTAssertEqualWithAccuracy(topWindow.origin.x, viewport.origin.x, 0.001);
|
|
792
|
+
XCTAssertEqualWithAccuracy(stickyWindow.origin.x, viewport.origin.x, 0.001);
|
|
793
|
+
XCTAssertEqualWithAccuracy(topWindow.origin.y, viewport.origin.y, 0.001);
|
|
794
|
+
XCTAssertEqualWithAccuracy(stickyWindow.origin.y, viewport.origin.y + 50, 0.001);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
horizontal.contentOffset = CGPointMake(640, 0);
|
|
798
|
+
[self.pager scrollViewDidEndDragging:horizontal willDecelerate:NO];
|
|
799
|
+
XCTestExpectation *settled = [self expectationWithDescription:@"external drag settles"];
|
|
800
|
+
dispatch_async(dispatch_get_main_queue(), ^{ [settled fulfill]; });
|
|
801
|
+
[self waitForExpectations:@[settled] timeout:2];
|
|
802
|
+
XCTAssertEqual([[self.pager valueForKey:@"currentIndex"] integerValue], 2);
|
|
803
|
+
XCTAssertEqual([[self.pager valueForKey:@"programmaticScrollEdgeOwners"] count], 0);
|
|
804
|
+
for (id owner in incomingOwners) {
|
|
805
|
+
XCTAssertNil([owner valueForKey:@"ownedTopHost"]);
|
|
806
|
+
XCTAssertNil([owner valueForKey:@"ownedStickyHost"]);
|
|
807
|
+
}
|
|
808
|
+
for (UIView *host in ownedHosts) {
|
|
809
|
+
XCTAssertNil(host.superview);
|
|
810
|
+
XCTAssertEqual(host.interactions.count, 0);
|
|
811
|
+
}
|
|
812
|
+
for (UIView *edgeMarker in ownedMarkers) XCTAssertNil(edgeMarker.superview);
|
|
813
|
+
for (UIScrollEdgeElementContainerInteraction *edge in ownedEdges) XCTAssertNil(edge.scrollView);
|
|
814
|
+
XCTAssertEqual(proxy.superview, [self.pager valueForKey:@"containerView"]);
|
|
815
|
+
XCTAssertNotNil(self.sticky.superview);
|
|
816
|
+
XCTAssertNil(top.scrollView);
|
|
817
|
+
XCTAssertNil(sticky.scrollView);
|
|
818
|
+
XCTAssertNil(marker.superview);
|
|
819
|
+
XCTAssertEqual(source.topEdgeEffect.style, UIScrollEdgeEffectStyle.softStyle);
|
|
820
|
+
UIScrollEdgeElementContainerInteraction *normal = [self.pager valueForKey:@"topEdgeInteraction"];
|
|
821
|
+
XCTAssertNotNil(normal);
|
|
822
|
+
XCTAssertEqual(normal.scrollView, target);
|
|
823
|
+
XCTAssertEqual([self.pager valueForKey:@"topEdgeScrollView"], target);
|
|
824
|
+
XCTAssertEqual([self.pager valueForKey:@"originalTopEdgeStyle"], UIScrollEdgeEffectStyle.softStyle);
|
|
825
|
+
XCTAssertEqual(target.topEdgeEffect.style, UIScrollEdgeEffectStyle.hardStyle);
|
|
826
|
+
UIScrollView *otherNeighbor = (UIScrollView *)pages[0].view.subviews.firstObject;
|
|
827
|
+
XCTAssertEqual(otherNeighbor.topEdgeEffect.style, UIScrollEdgeEffectStyle.softStyle);
|
|
828
|
+
XCTAssertNil(stickyMarker.superview);
|
|
829
|
+
XCTAssertEqual([self.pager valueForKey:@"topEdgeInteractionHost"], proxy);
|
|
830
|
+
XCTAssertEqual(overlay.superview, self.root);
|
|
831
|
+
XCTAssertEqual(overlay.interactions.count, 0);
|
|
832
|
+
} else {
|
|
833
|
+
XCTSkip(@"External direct Hard drag handoff requires iOS 27 or newer");
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
- (void)testExternalOverlayGeometryChangeReleasesBorrowedEdges
|
|
838
|
+
{
|
|
839
|
+
if (@available(iOS 27.0, *)) {
|
|
840
|
+
UIScrollView *source = nil;
|
|
841
|
+
UIView *overlay = nil;
|
|
842
|
+
UIScrollView *horizontal = [self prepareExternalDirectDragWithSource:&source overlay:&overlay];
|
|
843
|
+
UIScrollEdgeElementContainerInteraction *top = [self.pager valueForKey:@"topEdgeInteraction"];
|
|
844
|
+
UIView *pinned = [self.pager valueForKey:@"pinnedStickyHostView"];
|
|
845
|
+
[self.pager scrollViewWillBeginDragging:horizontal];
|
|
846
|
+
XCTAssertEqual([[self.pager valueForKey:@"programmaticScrollEdgeOwners"] count], 3);
|
|
847
|
+
id incoming = [[self.pager valueForKey:@"programmaticScrollEdgeOwners"] lastObject];
|
|
848
|
+
UIView *ownedTop = [incoming valueForKey:@"ownedTopHost"];
|
|
849
|
+
UIView *ownedSticky = [incoming valueForKey:@"ownedStickyHost"];
|
|
850
|
+
UIView *ownedMarker = [incoming valueForKey:@"topMarker"];
|
|
851
|
+
UIScrollEdgeElementContainerInteraction *ownedEdge = [incoming valueForKey:@"topInteraction"];
|
|
852
|
+
UIView *proxy = [self.pager valueForKey:@"topOverlayHostView"];
|
|
853
|
+
UIView *stickyParent = self.sticky.superview;
|
|
854
|
+
XCTAssertNotNil(ownedTop);
|
|
855
|
+
XCTAssertNotNil(ownedSticky);
|
|
856
|
+
overlay.frame = CGRectMake(0, 100, 320, 60);
|
|
857
|
+
XCTAssertTrue([self.pager resolveExternalTopOverlay]);
|
|
858
|
+
XCTAssertEqualWithAccuracy([[self.pager valueForKey:@"externalTopOverlayHeight"] doubleValue], 60, 0.001);
|
|
859
|
+
XCTAssertEqual([[self.pager valueForKey:@"programmaticScrollEdgeOwners"] count], 0);
|
|
860
|
+
XCTAssertNil(top.scrollView);
|
|
861
|
+
XCTAssertNil(ownedTop.superview);
|
|
862
|
+
XCTAssertNil(ownedSticky.superview);
|
|
863
|
+
XCTAssertEqual(ownedTop.interactions.count, 0);
|
|
864
|
+
XCTAssertEqual(ownedSticky.interactions.count, 0);
|
|
865
|
+
XCTAssertNil(ownedMarker.superview);
|
|
866
|
+
XCTAssertNil(ownedEdge.scrollView);
|
|
867
|
+
XCTAssertNil([incoming valueForKey:@"ownedTopHost"]);
|
|
868
|
+
XCTAssertNil([incoming valueForKey:@"ownedStickyHost"]);
|
|
869
|
+
XCTAssertEqual(proxy.superview, [self.pager valueForKey:@"containerView"]);
|
|
870
|
+
XCTAssertEqual(self.sticky.superview, stickyParent);
|
|
871
|
+
XCTAssertEqual(overlay.superview, self.root);
|
|
872
|
+
XCTAssertEqual(source.topEdgeEffect.style, UIScrollEdgeEffectStyle.softStyle);
|
|
873
|
+
XCTAssertEqual(pinned.superview, [self.pager valueForKey:@"containerView"]);
|
|
874
|
+
XCTAssertTrue(pinned.hidden);
|
|
875
|
+
} else {
|
|
876
|
+
XCTSkip(@"External direct Hard drag handoff requires iOS 27 or newer");
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
- (void)testExternalOverlayReplacementReleasesBorrowedEdges
|
|
881
|
+
{
|
|
882
|
+
if (@available(iOS 27.0, *)) {
|
|
883
|
+
UIScrollView *source = nil;
|
|
884
|
+
UIView *overlay = nil;
|
|
885
|
+
UIScrollView *horizontal = [self prepareExternalDirectDragWithSource:&source overlay:&overlay];
|
|
886
|
+
UIScrollEdgeElementContainerInteraction *top = [self.pager valueForKey:@"topEdgeInteraction"];
|
|
887
|
+
XCTAssertNotNil(top);
|
|
888
|
+
[self.pager scrollViewWillBeginDragging:horizontal];
|
|
889
|
+
XCTAssertEqual([[self.pager valueForKey:@"programmaticScrollEdgeOwners"] count], 3);
|
|
890
|
+
UIView *replacement = [[UIView alloc] initWithFrame:overlay.frame];
|
|
891
|
+
replacement.tag = overlay.tag;
|
|
892
|
+
[overlay removeFromSuperview];
|
|
893
|
+
[self.root addSubview:replacement];
|
|
894
|
+
XCTAssertTrue([self.pager resolveExternalTopOverlay]);
|
|
895
|
+
XCTAssertEqual([self.pager valueForKey:@"externalTopOverlayView"], replacement);
|
|
896
|
+
XCTAssertEqualWithAccuracy([[self.pager valueForKey:@"externalTopOverlayHeight"] doubleValue], 50, 0.001);
|
|
897
|
+
XCTAssertEqual([[self.pager valueForKey:@"programmaticScrollEdgeOwners"] count], 0);
|
|
898
|
+
XCTAssertNil(top.scrollView);
|
|
899
|
+
XCTAssertEqual(source.topEdgeEffect.style, UIScrollEdgeEffectStyle.softStyle);
|
|
900
|
+
XCTAssertEqual(replacement.interactions.count, 0);
|
|
901
|
+
XCTAssertEqual(replacement.superview, self.root);
|
|
902
|
+
} else {
|
|
903
|
+
XCTSkip(@"External direct Hard drag handoff requires iOS 27 or newer");
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
- (void)testExternalOverlayTagChangeReleasesBorrowedEdges
|
|
908
|
+
{
|
|
909
|
+
if (@available(iOS 27.0, *)) {
|
|
910
|
+
UIScrollView *source = nil;
|
|
911
|
+
UIScrollView *horizontal = [self prepareExternalDirectDragWithSource:&source overlay:nil];
|
|
912
|
+
UIScrollEdgeElementContainerInteraction *top = [self.pager valueForKey:@"topEdgeInteraction"];
|
|
913
|
+
XCTAssertNotNil(top);
|
|
914
|
+
[self.pager scrollViewWillBeginDragging:horizontal];
|
|
915
|
+
XCTAssertEqual([[self.pager valueForKey:@"programmaticScrollEdgeOwners"] count], 3);
|
|
916
|
+
auto props = std::make_shared<RNCCollapsiblePagerViewProps>();
|
|
917
|
+
props->nativeSmoothHeaderScrollEnabled = true;
|
|
918
|
+
props->topScrollEdgeEffect = RNCCollapsiblePagerViewTopScrollEdgeEffect::Hard;
|
|
919
|
+
props->externalTopOverlayTag = 0;
|
|
920
|
+
[self.pager updateProps:props oldProps:nullptr];
|
|
921
|
+
XCTAssertEqual([[self.pager valueForKey:@"externalTopOverlayTag"] integerValue], 0);
|
|
922
|
+
XCTAssertEqual([[self.pager valueForKey:@"programmaticScrollEdgeOwners"] count], 0);
|
|
923
|
+
XCTAssertNil(top.scrollView);
|
|
924
|
+
XCTAssertEqual(source.topEdgeEffect.style, UIScrollEdgeEffectStyle.softStyle);
|
|
925
|
+
} else {
|
|
926
|
+
XCTSkip(@"External direct Hard drag handoff requires iOS 27 or newer");
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
- (void)testExternalOuterInactivityReleasesBorrowedEdges
|
|
931
|
+
{
|
|
932
|
+
if (@available(iOS 27.0, *)) {
|
|
933
|
+
UIScrollView *source = nil;
|
|
934
|
+
UIScrollView *horizontal = [self prepareExternalDirectDragWithSource:&source overlay:nil];
|
|
935
|
+
RNCPagerViewComponentView *outer = [RNCPagerViewComponentView new];
|
|
936
|
+
[self.pager setValue:outer forKey:@"externalOuterPager"];
|
|
937
|
+
[self.pager setValue:@YES forKey:@"externalOuterPageActive"];
|
|
938
|
+
[self.pager setValue:@NO forKey:@"externalOuterTransitioning"];
|
|
939
|
+
UIScrollEdgeElementContainerInteraction *top = [self.pager valueForKey:@"topEdgeInteraction"];
|
|
940
|
+
XCTAssertNotNil(top);
|
|
941
|
+
[self.pager scrollViewWillBeginDragging:horizontal];
|
|
942
|
+
XCTAssertEqual([[self.pager valueForKey:@"programmaticScrollEdgeOwners"] count], 3);
|
|
943
|
+
[self.pager outerPagerDidUpdateOverlayOwner:NO transitioning:NO];
|
|
944
|
+
XCTAssertEqual([[self.pager valueForKey:@"programmaticScrollEdgeOwners"] count], 0);
|
|
945
|
+
XCTAssertNil(top.scrollView);
|
|
946
|
+
XCTAssertNil([self.pager valueForKey:@"topEdgeInteraction"]);
|
|
947
|
+
XCTAssertEqual(source.topEdgeEffect.style, UIScrollEdgeEffectStyle.softStyle);
|
|
948
|
+
[self.pager beginProgrammaticScrollEdgesToIndex:2 allowExternalOverlay:YES];
|
|
949
|
+
XCTAssertEqual([[self.pager valueForKey:@"programmaticScrollEdgeOwners"] count], 0);
|
|
950
|
+
} else {
|
|
951
|
+
XCTSkip(@"External direct Hard drag handoff requires iOS 27 or newer");
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
- (void)testExternalOuterTransitionReleasesBorrowedEdges
|
|
956
|
+
{
|
|
957
|
+
if (@available(iOS 27.0, *)) {
|
|
958
|
+
UIScrollView *source = nil;
|
|
959
|
+
UIScrollView *horizontal = [self prepareExternalDirectDragWithSource:&source overlay:nil];
|
|
960
|
+
RNCPagerViewComponentView *outer = [RNCPagerViewComponentView new];
|
|
961
|
+
[self.pager setValue:outer forKey:@"externalOuterPager"];
|
|
962
|
+
[self.pager setValue:@YES forKey:@"externalOuterPageActive"];
|
|
963
|
+
[self.pager setValue:@NO forKey:@"externalOuterTransitioning"];
|
|
964
|
+
UIScrollEdgeElementContainerInteraction *top = [self.pager valueForKey:@"topEdgeInteraction"];
|
|
965
|
+
XCTAssertNotNil(top);
|
|
966
|
+
[self.pager scrollViewWillBeginDragging:horizontal];
|
|
967
|
+
XCTAssertEqual([[self.pager valueForKey:@"programmaticScrollEdgeOwners"] count], 3);
|
|
968
|
+
[self.pager outerPagerDidUpdateOverlayOwner:YES transitioning:YES];
|
|
969
|
+
XCTAssertEqual([[self.pager valueForKey:@"programmaticScrollEdgeOwners"] count], 0);
|
|
970
|
+
XCTAssertNil(top.scrollView);
|
|
971
|
+
XCTAssertNil([self.pager valueForKey:@"topEdgeInteraction"]);
|
|
972
|
+
XCTAssertEqual(source.topEdgeEffect.style, UIScrollEdgeEffectStyle.softStyle);
|
|
973
|
+
[self.pager beginProgrammaticScrollEdgesToIndex:2 allowExternalOverlay:YES];
|
|
974
|
+
XCTAssertEqual([[self.pager valueForKey:@"programmaticScrollEdgeOwners"] count], 0);
|
|
975
|
+
} else {
|
|
976
|
+
XCTSkip(@"External direct Hard drag handoff requires iOS 27 or newer");
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
- (void)testDirectDragAdoptsSourceBeforeDetachAndOwnsBothNeighbours
|
|
981
|
+
{
|
|
982
|
+
if (@available(iOS 27.0, *)) {
|
|
983
|
+
UIScrollView *source = nil;
|
|
984
|
+
UIScrollView *horizontal = [self prepareDirectDragWithSource:&source];
|
|
985
|
+
UIView *topHost = [self.pager valueForKey:@"topEdgeInteractionHost"];
|
|
986
|
+
UIView *pinnedHost = [self.pager valueForKey:@"pinnedStickyHostView"];
|
|
987
|
+
UIScrollEdgeElementContainerInteraction *top = [self.pager valueForKey:@"topEdgeInteraction"];
|
|
988
|
+
UIScrollEdgeElementContainerInteraction *sticky = [self.pager valueForKey:@"stickyEdgeInteraction"];
|
|
989
|
+
UIView *topMarker = [self.pager valueForKey:@"topEdgeMarker"];
|
|
990
|
+
UIView *stickyMarker = [self.pager valueForKey:@"stickyEdgeMarker"];
|
|
991
|
+
XCTAssertNotNil(top);
|
|
992
|
+
XCTAssertNotNil(sticky);
|
|
993
|
+
|
|
994
|
+
[self.pager scrollViewWillBeginDragging:horizontal];
|
|
995
|
+
|
|
996
|
+
NSArray *owners = [self.pager valueForKey:@"programmaticScrollEdgeOwners"];
|
|
997
|
+
XCTAssertEqual(owners.count, 3);
|
|
998
|
+
id sourceOwner = [owners firstObject];
|
|
999
|
+
XCTAssertEqual([sourceOwner valueForKey:@"topInteraction"], top);
|
|
1000
|
+
XCTAssertEqual([sourceOwner valueForKey:@"stickyInteraction"], sticky);
|
|
1001
|
+
XCTAssertEqual([sourceOwner valueForKey:@"topMarker"], topMarker);
|
|
1002
|
+
XCTAssertEqual([sourceOwner valueForKey:@"stickyMarker"], stickyMarker);
|
|
1003
|
+
XCTAssertEqual([sourceOwner valueForKey:@"originalStyle"], UIScrollEdgeEffectStyle.softStyle);
|
|
1004
|
+
XCTAssertTrue([[sourceOwner valueForKey:@"borrowsPinnedStickyHost"] boolValue]);
|
|
1005
|
+
XCTAssertTrue([topHost.interactions containsObject:top]);
|
|
1006
|
+
XCTAssertTrue([pinnedHost.interactions containsObject:sticky]);
|
|
1007
|
+
XCTAssertEqual(top.scrollView, source);
|
|
1008
|
+
XCTAssertEqual(sticky.scrollView, source);
|
|
1009
|
+
XCTAssertEqual(topMarker.superview, topHost);
|
|
1010
|
+
XCTAssertEqual(stickyMarker.superview, pinnedHost);
|
|
1011
|
+
XCTAssertEqual(pinnedHost.superview, source);
|
|
1012
|
+
XCTAssertFalse(pinnedHost.hidden);
|
|
1013
|
+
for (id owner in owners) {
|
|
1014
|
+
XCTAssertNotNil([owner valueForKey:@"topInteraction"]);
|
|
1015
|
+
XCTAssertNotNil([[owner valueForKey:@"topInteraction"] scrollView]);
|
|
1016
|
+
}
|
|
1017
|
+
} else {
|
|
1018
|
+
XCTSkip(@"Direct local Hard drag handoff requires iOS 27 or newer");
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
- (void)testDirectDragTakeoverIgnoresOldSettlementAndReleasesOnReturn
|
|
1023
|
+
{
|
|
1024
|
+
if (@available(iOS 27.0, *)) {
|
|
1025
|
+
UIScrollView *source = nil;
|
|
1026
|
+
UIScrollView *horizontal = [self prepareDirectDragWithSource:&source];
|
|
1027
|
+
UIScrollEdgeElementContainerInteraction *top = [self.pager valueForKey:@"topEdgeInteraction"];
|
|
1028
|
+
UIView *topHost = [self.pager valueForKey:@"topEdgeInteractionHost"];
|
|
1029
|
+
NSUInteger originalTransition = [[self.pager valueForKey:@"transitionId"] unsignedIntegerValue];
|
|
1030
|
+
XCTAssertEqual([[self.pager valueForKey:@"pageControllers"] count], 3);
|
|
1031
|
+
XCTAssertEqualWithAccuracy(horizontal.contentOffset.x, 320, 0.001);
|
|
1032
|
+
XCTAssertEqual([[self.pager valueForKey:@"currentIndex"] integerValue], 1);
|
|
1033
|
+
[self.pager scrollViewWillBeginDragging:horizontal];
|
|
1034
|
+
[self.pager scrollViewDidEndDragging:horizontal willDecelerate:NO];
|
|
1035
|
+
[self.pager scrollViewWillBeginDragging:horizontal];
|
|
1036
|
+
XCTAssertGreaterThan([[self.pager valueForKey:@"transitionId"] unsignedIntegerValue], originalTransition + 1);
|
|
1037
|
+
XCTAssertTrue([topHost.interactions containsObject:top]);
|
|
1038
|
+
XCTAssertEqual(top.scrollView, source);
|
|
1039
|
+
XCTAssertEqual([[self.pager valueForKey:@"programmaticScrollEdgeOwners"] count], 3);
|
|
1040
|
+
|
|
1041
|
+
XCTestExpectation *oldSettlement = [self expectationWithDescription:@"old settlement skipped"];
|
|
1042
|
+
dispatch_async(dispatch_get_main_queue(), ^{ [oldSettlement fulfill]; });
|
|
1043
|
+
[self waitForExpectations:@[oldSettlement] timeout:2];
|
|
1044
|
+
XCTAssertEqualWithAccuracy(horizontal.contentOffset.x, 320, 0.001);
|
|
1045
|
+
XCTAssertEqual([[self.pager valueForKey:@"currentIndex"] integerValue], 1);
|
|
1046
|
+
XCTAssertEqual([[self.pager valueForKey:@"programmaticScrollEdgeOwners"] count], 3);
|
|
1047
|
+
[self.pager scrollViewDidEndDragging:horizontal willDecelerate:YES];
|
|
1048
|
+
XCTAssertEqual([[self.pager valueForKey:@"programmaticScrollEdgeOwners"] count], 3);
|
|
1049
|
+
[self.pager scrollViewDidEndDecelerating:horizontal];
|
|
1050
|
+
XCTestExpectation *settled = [self expectationWithDescription:@"drag settled"];
|
|
1051
|
+
dispatch_async(dispatch_get_main_queue(), ^{ [settled fulfill]; });
|
|
1052
|
+
[self waitForExpectations:@[settled] timeout:2];
|
|
1053
|
+
XCTAssertEqual([[self.pager valueForKey:@"programmaticScrollEdgeOwners"] count], 0);
|
|
1054
|
+
XCTAssertFalse([topHost.interactions containsObject:top]);
|
|
1055
|
+
XCTAssertNil(top.scrollView);
|
|
1056
|
+
XCTAssertEqual([[self.pager valueForKey:@"currentIndex"] integerValue], 1);
|
|
1057
|
+
XCTAssertEqual([self.pager valueForKey:@"topEdgeScrollView"], source);
|
|
1058
|
+
XCTAssertEqual([self.pager valueForKey:@"originalTopEdgeStyle"], UIScrollEdgeEffectStyle.softStyle);
|
|
1059
|
+
UIScrollEdgeElementContainerInteraction *normal = [self.pager valueForKey:@"topEdgeInteraction"];
|
|
1060
|
+
XCTAssertNotNil(normal);
|
|
1061
|
+
XCTAssertEqual(normal.scrollView, source);
|
|
1062
|
+
XCTAssertEqual(source.topEdgeEffect.style, UIScrollEdgeEffectStyle.hardStyle);
|
|
1063
|
+
NSArray<UIViewController *> *pages = [self.pager valueForKey:@"pageControllers"];
|
|
1064
|
+
for (NSNumber *index in @[@0, @2]) {
|
|
1065
|
+
UIScrollView *neighbor = (UIScrollView *)pages[index.integerValue].view.subviews.firstObject;
|
|
1066
|
+
XCTAssertEqual(neighbor.topEdgeEffect.style, UIScrollEdgeEffectStyle.softStyle);
|
|
1067
|
+
}
|
|
1068
|
+
} else {
|
|
1069
|
+
XCTSkip(@"Direct local Hard drag handoff requires iOS 27 or newer");
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
- (void)testAnimatedDirectPageCommandBorrowsRealSourceEdgeUntilCompletion
|
|
1074
|
+
{
|
|
1075
|
+
if (@available(iOS 27.0, *)) {
|
|
1076
|
+
UIScrollView *source = [self preparePinnedLocalHardSource];
|
|
1077
|
+
UIView *topHost = [self.pager valueForKey:@"topEdgeInteractionHost"];
|
|
1078
|
+
UIView *pinnedHost = [self.pager valueForKey:@"pinnedStickyHostView"];
|
|
1079
|
+
UIScrollEdgeElementContainerInteraction *top = [self.pager valueForKey:@"topEdgeInteraction"];
|
|
1080
|
+
UIScrollEdgeElementContainerInteraction *sticky = [self.pager valueForKey:@"stickyEdgeInteraction"];
|
|
1081
|
+
UIView *topMarker = [self.pager valueForKey:@"topEdgeMarker"];
|
|
1082
|
+
UIView *stickyMarker = [self.pager valueForKey:@"stickyEdgeMarker"];
|
|
1083
|
+
XCTAssertNotNil(top);
|
|
1084
|
+
XCTAssertNotNil(sticky);
|
|
1085
|
+
XCTAssertEqual([self.pager valueForKey:@"originalTopEdgeStyle"], UIScrollEdgeEffectStyle.softStyle);
|
|
1086
|
+
|
|
1087
|
+
[self.pager adoptCurrentScrollEdgeForProgrammaticTransition:NO];
|
|
1088
|
+
id owner = [[self.pager valueForKey:@"programmaticScrollEdgeOwners"] firstObject];
|
|
1089
|
+
XCTAssertNotNil(owner);
|
|
1090
|
+
XCTAssertEqual([owner valueForKey:@"topInteraction"], top);
|
|
1091
|
+
XCTAssertEqual([owner valueForKey:@"stickyInteraction"], sticky);
|
|
1092
|
+
XCTAssertEqual([owner valueForKey:@"topMarker"], topMarker);
|
|
1093
|
+
XCTAssertEqual([owner valueForKey:@"stickyMarker"], stickyMarker);
|
|
1094
|
+
XCTAssertEqual([owner valueForKey:@"originalStyle"], UIScrollEdgeEffectStyle.softStyle);
|
|
1095
|
+
XCTAssertTrue([[owner valueForKey:@"borrowsPinnedStickyHost"] boolValue]);
|
|
1096
|
+
XCTAssertTrue([topHost.interactions containsObject:top]);
|
|
1097
|
+
XCTAssertTrue([pinnedHost.interactions containsObject:sticky]);
|
|
1098
|
+
XCTAssertEqual(top.scrollView, source);
|
|
1099
|
+
XCTAssertEqual(sticky.scrollView, source);
|
|
1100
|
+
XCTAssertEqual(topMarker.superview, topHost);
|
|
1101
|
+
XCTAssertEqual(stickyMarker.superview, pinnedHost);
|
|
1102
|
+
XCTAssertEqual(source.topEdgeEffect.style, UIScrollEdgeEffectStyle.hardStyle);
|
|
1103
|
+
|
|
1104
|
+
[self.pager restorePinnedStickyHeader];
|
|
1105
|
+
XCTAssertEqual(pinnedHost.superview, source);
|
|
1106
|
+
XCTAssertFalse(pinnedHost.hidden);
|
|
1107
|
+
[self.pager setValue:nil forKey:@"observedScrollView"];
|
|
1108
|
+
[self.pager finishProgrammaticScrollEdges];
|
|
1109
|
+
XCTAssertFalse([topHost.interactions containsObject:top]);
|
|
1110
|
+
XCTAssertFalse([pinnedHost.interactions containsObject:sticky]);
|
|
1111
|
+
XCTAssertNil(top.scrollView);
|
|
1112
|
+
XCTAssertNil(sticky.scrollView);
|
|
1113
|
+
XCTAssertNil(topMarker.superview);
|
|
1114
|
+
XCTAssertNil(stickyMarker.superview);
|
|
1115
|
+
XCTAssertEqual(source.topEdgeEffect.style, UIScrollEdgeEffectStyle.softStyle);
|
|
1116
|
+
XCTAssertEqual(pinnedHost.superview, [self.pager valueForKey:@"containerView"]);
|
|
1117
|
+
XCTAssertTrue(pinnedHost.hidden);
|
|
1118
|
+
XCTAssertEqual([[self.pager valueForKey:@"programmaticScrollEdgeOwners"] count], 0);
|
|
1119
|
+
} else {
|
|
1120
|
+
XCTSkip(@"Animated local Hard edge handoff requires iOS 27 or newer");
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
- (void)testCancelledDirectPageCommandReleasesBorrowedSourceEdge
|
|
1125
|
+
{
|
|
1126
|
+
if (@available(iOS 27.0, *)) {
|
|
1127
|
+
UIScrollView *source = [self preparePinnedLocalHardSource];
|
|
1128
|
+
UIView *topHost = [self.pager valueForKey:@"topEdgeInteractionHost"];
|
|
1129
|
+
UIView *pinnedHost = [self.pager valueForKey:@"pinnedStickyHostView"];
|
|
1130
|
+
UIScrollEdgeElementContainerInteraction *top = [self.pager valueForKey:@"topEdgeInteraction"];
|
|
1131
|
+
UIScrollEdgeElementContainerInteraction *sticky = [self.pager valueForKey:@"stickyEdgeInteraction"];
|
|
1132
|
+
UIView *topMarker = [self.pager valueForKey:@"topEdgeMarker"];
|
|
1133
|
+
UIView *stickyMarker = [self.pager valueForKey:@"stickyEdgeMarker"];
|
|
1134
|
+
[self.pager adoptCurrentScrollEdgeForProgrammaticTransition:NO];
|
|
1135
|
+
[self.pager restorePinnedStickyHeader];
|
|
1136
|
+
[self.pager clearProgrammaticScrollEdges];
|
|
1137
|
+
XCTAssertFalse([topHost.interactions containsObject:top]);
|
|
1138
|
+
XCTAssertFalse([pinnedHost.interactions containsObject:sticky]);
|
|
1139
|
+
XCTAssertNil(topMarker.superview);
|
|
1140
|
+
XCTAssertNil(stickyMarker.superview);
|
|
1141
|
+
XCTAssertEqual(source.topEdgeEffect.style, UIScrollEdgeEffectStyle.softStyle);
|
|
1142
|
+
XCTAssertEqual(pinnedHost.superview, [self.pager valueForKey:@"containerView"]);
|
|
1143
|
+
XCTAssertTrue(pinnedHost.hidden);
|
|
1144
|
+
} else {
|
|
1145
|
+
XCTSkip(@"Animated local Hard edge handoff requires iOS 27 or newer");
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
|
|
393
1149
|
- (void)testIOS27LocalHardThemeBackgroundTracksMaskedHeaderGeometry
|
|
394
1150
|
{
|
|
395
1151
|
if (@available(iOS 27.0, *)) {
|