@onekeyfe/react-native-pager-view 6.7.0-2 → 6.7.0-21
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 +14 -0
- package/android/src/androidTest/AndroidManifest.xml +7 -0
- package/android/src/androidTest/java/com/reactnativepagerview/CollapsiblePagerTabReservationTest.kt +306 -0
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerHost.kt +130 -30
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerNativeHeaders.kt +72 -7
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerViewManager.kt +27 -0
- package/android/src/main/java/com/reactnativepagerview/NativeScrollerScrollAwayInsets.kt +19 -0
- package/android/src/main/java/com/reactnativepagerview/NativeScrollerView.kt +50 -6
- package/android/src/main/java/com/reactnativepagerview/NestedScrollableHost.kt +8 -2
- package/android/src/test/java/com/reactnativepagerview/NativeScrollerScrollAwayInsetsTest.kt +40 -0
- package/docs/SPEC.md +257 -10
- package/ios/RNCCollapsiblePagerViewComponentView.mm +614 -42
- package/ios/RNCNativeScrollerComponentView.h +2 -0
- package/ios/RNCNativeScrollerComponentView.mm +42 -0
- package/ios/tests/RNCCollapsiblePagerFirstFrameTests.mm +403 -1
- package/ios/tests/RNCCollapsiblePagerHitTestTests.mm +338 -0
- package/ios/tests/RNCCollapsiblePagerPressCancellationTests.mm +74 -9
- package/ios/tests/RNCCollapsiblePagerRefreshControlTests.mm +50 -0
- package/lib/module/CollapsiblePagerView.js +32 -7
- package/lib/module/CollapsiblePagerView.web.js +439 -39
- package/lib/module/CollapsiblePagerViewNativeComponent.ts +13 -0
- package/lib/module/NativeScroller.web.js +16 -3
- package/lib/module/WebCollapsiblePagerPage.js +182 -0
- package/lib/module/index.js +2 -0
- package/lib/module/index.native.js +9 -0
- package/lib/module/index.web.js +2 -0
- package/lib/typescript/src/CollapsiblePagerView.d.ts +10 -3
- package/lib/typescript/src/CollapsiblePagerView.web.d.ts +66 -3
- package/lib/typescript/src/CollapsiblePagerViewNativeComponent.d.ts +5 -0
- package/lib/typescript/src/WebCollapsiblePagerPage.d.ts +42 -0
- package/lib/typescript/src/index.d.ts +4 -0
- package/lib/typescript/src/index.native.d.ts +13 -0
- package/lib/typescript/src/index.web.d.ts +4 -0
- package/package.json +6 -4
- package/react-native-pager-view.podspec +1 -0
- package/src/CollapsiblePagerView.tsx +99 -8
- package/src/CollapsiblePagerView.web.tsx +657 -51
- package/src/CollapsiblePagerViewNativeComponent.ts +13 -0
- package/src/NativeScroller.web.tsx +24 -2
- package/src/WebCollapsiblePagerPage.tsx +258 -0
- package/src/index.native.tsx +30 -0
- package/src/index.tsx +4 -0
- package/src/index.web.tsx +4 -0
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
// OneKey patch: deduplicate structural mutation parents before scanning the page.
|
|
20
20
|
#include <unordered_set>
|
|
21
|
+
#include <cmath>
|
|
21
22
|
|
|
22
23
|
using namespace facebook::react;
|
|
23
24
|
|
|
@@ -25,6 +26,7 @@ static void *RNCCollapsiblePagerContentOffsetContext = &RNCCollapsiblePagerConte
|
|
|
25
26
|
static void *RNCRefreshControlLayerContext = &RNCRefreshControlLayerContext;
|
|
26
27
|
static void *RNCScrollRefreshControlContext = &RNCScrollRefreshControlContext;
|
|
27
28
|
static char RNCRefreshControlOffsetKey;
|
|
29
|
+
static void RNCCollapsiblePagerLog(NSString *message);
|
|
28
30
|
|
|
29
31
|
@interface RNCRefreshControlOffsetState : NSObject
|
|
30
32
|
+ (void)setOffset:(CGFloat)offset forControl:(UIRefreshControl *)control owner:(id)owner;
|
|
@@ -39,6 +41,10 @@ static char RNCRefreshControlOffsetKey;
|
|
|
39
41
|
BOOL _originalMasksToBounds;
|
|
40
42
|
BOOL _applying;
|
|
41
43
|
BOOL _observing;
|
|
44
|
+
CGFloat _lastLoggedOffset;
|
|
45
|
+
NSUInteger _lastLoggedOwnerCount;
|
|
46
|
+
CGFloat _lastLoggedTransformY;
|
|
47
|
+
BOOL _hasLoggedState;
|
|
42
48
|
}
|
|
43
49
|
|
|
44
50
|
- (instancetype)initWithControl:(UIRefreshControl *)control
|
|
@@ -79,6 +85,21 @@ static char RNCRefreshControlOffsetKey;
|
|
|
79
85
|
}
|
|
80
86
|
[CATransaction commit];
|
|
81
87
|
_applying = NO;
|
|
88
|
+
#if DEBUG
|
|
89
|
+
if (!_hasLoggedState || fabs(_lastLoggedOffset - offset) > 0.01 ||
|
|
90
|
+
_lastLoggedOwnerCount != _offsets.count ||
|
|
91
|
+
fabs(_lastLoggedTransformY - transform.m42) > 0.01) {
|
|
92
|
+
_hasLoggedState = YES;
|
|
93
|
+
_lastLoggedOffset = offset;
|
|
94
|
+
_lastLoggedOwnerCount = _offsets.count;
|
|
95
|
+
_lastLoggedTransformY = transform.m42;
|
|
96
|
+
RNCCollapsiblePagerLog([NSString stringWithFormat:
|
|
97
|
+
@"[IOS-HOME-VISUAL] epochMs=%.0f monoMs=%.0f phase=refresh-transform ownerCount=%lu offsetSum=%.1f baselineY=%.1f appliedY=%.1f masksToBounds=%d",
|
|
98
|
+
[NSDate.date timeIntervalSince1970] * 1000, CACurrentMediaTime() * 1000,
|
|
99
|
+
(unsigned long)_offsets.count, offset, _baselineTransform.m42,
|
|
100
|
+
transform.m42, control.layer.masksToBounds]);
|
|
101
|
+
}
|
|
102
|
+
#endif
|
|
82
103
|
}
|
|
83
104
|
|
|
84
105
|
+ (void)setOffset:(CGFloat)offset forControl:(UIRefreshControl *)control owner:(id)owner
|
|
@@ -137,6 +158,20 @@ static NSString *const RNCCollapsiblePagerNativeScrollerIDPrefix =
|
|
|
137
158
|
|
|
138
159
|
static const CFTimeInterval RNCCollapsiblePagerFallbackDebounceInterval = 0.5;
|
|
139
160
|
|
|
161
|
+
typedef NS_ENUM(NSInteger, RNCScrollCandidateTier) {
|
|
162
|
+
RNCScrollCandidateTierNone = 0,
|
|
163
|
+
RNCScrollCandidateTierGenericFallback = 1,
|
|
164
|
+
RNCScrollCandidateTierVerifiedNativeList = 2,
|
|
165
|
+
RNCScrollCandidateTierFabricScrollView = 3,
|
|
166
|
+
RNCScrollCandidateTierExplicitScroller = 4,
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
struct RNCScrollCandidateResolution {
|
|
170
|
+
UIScrollView *scrollView = nil;
|
|
171
|
+
RNCScrollCandidateTier tier = RNCScrollCandidateTierNone;
|
|
172
|
+
BOOL hasUnreadyScrollHost = NO;
|
|
173
|
+
};
|
|
174
|
+
|
|
140
175
|
// OneKey patch: native scroll views can survive a move or be reused for a new
|
|
141
176
|
// Fabric component. Keep weak identity until their page is attached again.
|
|
142
177
|
@interface RNCCollapsiblePagerReleasedScrollState : NSObject
|
|
@@ -348,7 +383,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
348
383
|
@property (nonatomic, copy) RNCCollapsiblePagerNativeTabPressHandler onTabPress;
|
|
349
384
|
- (void)updateItemsJSON:(NSString *)itemsJSON;
|
|
350
385
|
- (void)updateStyleWithHeight:(CGFloat)height
|
|
386
|
+
variant:(NSString *)variant
|
|
351
387
|
contentPaddingHorizontal:(CGFloat)contentPaddingHorizontal
|
|
388
|
+
contentPaddingVertical:(CGFloat)contentPaddingVertical
|
|
352
389
|
itemSpacing:(CGFloat)itemSpacing
|
|
353
390
|
fontSize:(CGFloat)fontSize
|
|
354
391
|
fontFamily:(NSString *)fontFamily
|
|
@@ -365,12 +402,15 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
365
402
|
|
|
366
403
|
@implementation RNCCollapsiblePagerNativeTabBarView {
|
|
367
404
|
RNCCollapsiblePagerHorizontalScrollView *_scrollView;
|
|
405
|
+
UIView *_trackView;
|
|
368
406
|
UIView *_indicatorView;
|
|
369
407
|
NSArray<NSDictionary *> *_items;
|
|
370
408
|
NSMutableArray<UIButton *> *_buttons;
|
|
371
409
|
NSMutableArray<NSValue *> *_indicatorFrames;
|
|
372
410
|
CGFloat _barHeight;
|
|
373
411
|
CGFloat _contentPaddingHorizontal;
|
|
412
|
+
CGFloat _contentPaddingVertical;
|
|
413
|
+
BOOL _segmented;
|
|
374
414
|
CGFloat _itemSpacing;
|
|
375
415
|
CGFloat _fontSize;
|
|
376
416
|
NSString *_fontFamily;
|
|
@@ -393,6 +433,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
393
433
|
_indicatorFrames = [NSMutableArray new];
|
|
394
434
|
_barHeight = 44;
|
|
395
435
|
_contentPaddingHorizontal = 20;
|
|
436
|
+
_contentPaddingVertical = 0;
|
|
437
|
+
_segmented = NO;
|
|
396
438
|
_itemSpacing = 8;
|
|
397
439
|
_fontSize = 16;
|
|
398
440
|
_activeTextColor = UIColor.labelColor;
|
|
@@ -414,6 +456,10 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
414
456
|
_scrollView.delegate = self;
|
|
415
457
|
[self addSubview:_scrollView];
|
|
416
458
|
|
|
459
|
+
_trackView = [UIView new];
|
|
460
|
+
_trackView.userInteractionEnabled = NO;
|
|
461
|
+
[_scrollView addSubview:_trackView];
|
|
462
|
+
|
|
417
463
|
_indicatorView = [UIView new];
|
|
418
464
|
_indicatorView.userInteractionEnabled = NO;
|
|
419
465
|
[_scrollView addSubview:_indicatorView];
|
|
@@ -468,7 +514,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
468
514
|
}
|
|
469
515
|
|
|
470
516
|
- (void)updateStyleWithHeight:(CGFloat)height
|
|
517
|
+
variant:(NSString *)variant
|
|
471
518
|
contentPaddingHorizontal:(CGFloat)contentPaddingHorizontal
|
|
519
|
+
contentPaddingVertical:(CGFloat)contentPaddingVertical
|
|
472
520
|
itemSpacing:(CGFloat)itemSpacing
|
|
473
521
|
fontSize:(CGFloat)fontSize
|
|
474
522
|
fontFamily:(NSString *)fontFamily
|
|
@@ -480,19 +528,26 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
480
528
|
indicatorBottom:(CGFloat)indicatorBottom
|
|
481
529
|
{
|
|
482
530
|
_barHeight = MAX(1, height);
|
|
531
|
+
BOOL wasSegmented = _segmented;
|
|
532
|
+
_segmented = [variant isEqualToString:@"segmented"];
|
|
483
533
|
_contentPaddingHorizontal = MAX(0, contentPaddingHorizontal);
|
|
534
|
+
_contentPaddingVertical = MAX(0, contentPaddingVertical);
|
|
484
535
|
_itemSpacing = MAX(0, itemSpacing);
|
|
485
536
|
_fontSize = MAX(1, fontSize);
|
|
486
537
|
_fontFamily = [fontFamily copy];
|
|
487
|
-
self.backgroundColor = backgroundColor ?: UIColor.clearColor;
|
|
538
|
+
self.backgroundColor = _segmented ? UIColor.clearColor : (backgroundColor ?: UIColor.clearColor);
|
|
488
539
|
_scrollView.backgroundColor = self.backgroundColor;
|
|
540
|
+
_scrollView.scrollEnabled = !_segmented;
|
|
541
|
+
if (_segmented && !wasSegmented) [_scrollView setContentOffset:CGPointZero animated:NO];
|
|
542
|
+
_trackView.hidden = !_segmented;
|
|
543
|
+
_trackView.backgroundColor = backgroundColor ?: UIColor.clearColor;
|
|
489
544
|
_activeTextColor = activeTextColor ?: UIColor.labelColor;
|
|
490
545
|
_inactiveTextColor = inactiveTextColor ?: UIColor.secondaryLabelColor;
|
|
491
546
|
_indicatorColor = indicatorColor ?: _activeTextColor;
|
|
492
547
|
_indicatorHeight = MAX(0, indicatorHeight);
|
|
493
548
|
_indicatorBottom = MAX(0, indicatorBottom);
|
|
494
549
|
_indicatorView.backgroundColor = _indicatorColor;
|
|
495
|
-
_indicatorView.layer.cornerRadius = _indicatorHeight / 2;
|
|
550
|
+
_indicatorView.layer.cornerRadius = _segmented ? 0 : _indicatorHeight / 2;
|
|
496
551
|
for (UIButton *button in _buttons) button.titleLabel.font = self.tabFont;
|
|
497
552
|
[self setNeedsLayout];
|
|
498
553
|
}
|
|
@@ -551,6 +606,33 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
551
606
|
{
|
|
552
607
|
[super layoutSubviews];
|
|
553
608
|
_scrollView.frame = self.bounds;
|
|
609
|
+
if (_segmented) {
|
|
610
|
+
CGFloat width = CGRectGetWidth(self.bounds);
|
|
611
|
+
CGFloat height = CGRectGetHeight(self.bounds);
|
|
612
|
+
CGFloat insetX = MIN(_contentPaddingHorizontal, width / 2);
|
|
613
|
+
CGFloat insetY = MIN(_contentPaddingVertical, height / 2);
|
|
614
|
+
CGRect trackFrame = CGRectMake(insetX, insetY,
|
|
615
|
+
MAX(0, width - 2 * insetX), MAX(0, height - 2 * insetY));
|
|
616
|
+
_trackView.frame = trackFrame;
|
|
617
|
+
_trackView.layer.cornerRadius = CGRectGetHeight(trackFrame) / 2;
|
|
618
|
+
_scrollView.contentSize = self.bounds.size;
|
|
619
|
+
[_indicatorFrames removeAllObjects];
|
|
620
|
+
NSUInteger count = _buttons.count;
|
|
621
|
+
[_buttons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger index, BOOL *stop) {
|
|
622
|
+
CGFloat first = CGRectGetMinX(trackFrame) + CGRectGetWidth(trackFrame) * index / count;
|
|
623
|
+
CGFloat last = CGRectGetMinX(trackFrame) + CGRectGetWidth(trackFrame) * (index + 1) / count;
|
|
624
|
+
CGFloat x = self->_leftToRight ? first : CGRectGetMaxX(trackFrame) - (last - CGRectGetMinX(trackFrame));
|
|
625
|
+
CGFloat end = self->_leftToRight ? last : CGRectGetMaxX(trackFrame) - (first - CGRectGetMinX(trackFrame));
|
|
626
|
+
button.frame = CGRectMake(x, CGRectGetMinY(trackFrame), MAX(0, end - x), CGRectGetHeight(trackFrame));
|
|
627
|
+
CGRect thumbFrame = CGRectMake(x + 2, CGRectGetMinY(trackFrame) + 2,
|
|
628
|
+
MAX(0, CGRectGetWidth(button.frame) - 4), MAX(0, CGRectGetHeight(trackFrame) - 4));
|
|
629
|
+
[self->_indicatorFrames addObject:[NSValue valueWithCGRect:thumbFrame]];
|
|
630
|
+
}];
|
|
631
|
+
_indicatorView.layer.cornerRadius = MAX(0, CGRectGetHeight(trackFrame) - 4) / 2;
|
|
632
|
+
[self updatePresentation];
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
_trackView.frame = CGRectZero;
|
|
554
636
|
UIFont *font = self.tabFont;
|
|
555
637
|
NSMutableArray<NSNumber *> *widths = [NSMutableArray arrayWithCapacity:_buttons.count];
|
|
556
638
|
CGFloat itemsWidth = 0;
|
|
@@ -614,10 +696,10 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
614
696
|
CGRectGetMinX(fromFrame) + (CGRectGetMinX(toFrame) - CGRectGetMinX(fromFrame)) * fraction,
|
|
615
697
|
CGRectGetMinY(fromFrame),
|
|
616
698
|
CGRectGetWidth(fromFrame) + (CGRectGetWidth(toFrame) - CGRectGetWidth(fromFrame)) * fraction,
|
|
617
|
-
_indicatorHeight
|
|
699
|
+
_segmented ? CGRectGetHeight(fromFrame) : _indicatorHeight
|
|
618
700
|
);
|
|
619
701
|
_indicatorView.frame = indicatorFrame;
|
|
620
|
-
_indicatorView.hidden =
|
|
702
|
+
_indicatorView.hidden = CGRectGetHeight(indicatorFrame) <= 0;
|
|
621
703
|
|
|
622
704
|
NSInteger selectedIndex = (NSInteger)round(progress);
|
|
623
705
|
[_buttons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger index, BOOL *stop) {
|
|
@@ -634,7 +716,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
634
716
|
: UIAccessibilityTraitButton;
|
|
635
717
|
}];
|
|
636
718
|
|
|
637
|
-
if (_shouldCenterSelectedItem && !_scrollView.tracking && !_scrollView.dragging &&
|
|
719
|
+
if (_shouldCenterSelectedItem && !_segmented && !_scrollView.tracking && !_scrollView.dragging &&
|
|
638
720
|
!_scrollView.decelerating && CGRectGetWidth(_scrollView.bounds) > 0) {
|
|
639
721
|
CGFloat maximumOffset = MAX(0, _scrollView.contentSize.width - CGRectGetWidth(_scrollView.bounds));
|
|
640
722
|
CGFloat desiredOffset = RNCClamp(
|
|
@@ -1451,23 +1533,29 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1451
1533
|
- (void)liftSharedHeadersForPagerTransition:(NSString *)reason;
|
|
1452
1534
|
- (void)restoreSharedHeadersToContainer;
|
|
1453
1535
|
- (void)layoutSharedHeaders;
|
|
1536
|
+
- (void)bringReservedStickyControlToFront;
|
|
1454
1537
|
- (void)restorePinnedStickyHeader;
|
|
1455
1538
|
- (void)updatePinnedStickyHeader;
|
|
1456
1539
|
- (UIScrollView *)currentVerticalScrollViewForParentPager;
|
|
1457
1540
|
- (UIScrollView *)verticalScrollViewForPageAtIndex:(NSInteger)pageIndex;
|
|
1541
|
+
- (RNCScrollCandidateResolution)resolveScrollCandidateForPageAtIndex:(NSInteger)pageIndex;
|
|
1542
|
+
- (RNCScrollCandidateResolution)resolveScrollCandidateInView:(UIView *)view;
|
|
1458
1543
|
- (UIScrollView *)findExplicitNativeScrollerInView:(UIView *)view;
|
|
1459
1544
|
- (UIScrollView *)findFabricScrollViewInView:(UIView *)view;
|
|
1460
1545
|
- (UIScrollView *)findVerticalScrollViewInView:(UIView *)view;
|
|
1546
|
+
- (void)scheduleCoalescedScrollReconcile;
|
|
1461
1547
|
- (void)scheduleScrollObserverRetryForCurrentPage;
|
|
1462
1548
|
- (void)preparePageForHorizontalTransitionAtIndex:(NSInteger)pageIndex;
|
|
1463
1549
|
- (void)updateSharedHeaderPressCancellationGesture;
|
|
1464
1550
|
- (void)detachSharedHeaderPressCancellationGesture;
|
|
1465
1551
|
- (void)updateRefreshControlForScrollView:(UIScrollView *)scrollView;
|
|
1466
1552
|
- (void)releaseRefreshControlForScrollView:(UIScrollView *)scrollView;
|
|
1553
|
+
- (void)logHomeVisualGeometry:(NSString *)phase scrollView:(UIScrollView *)scrollView;
|
|
1467
1554
|
- (void)cancelTopInsetReboundCheck;
|
|
1468
1555
|
- (void)scheduleTopInsetReboundCheck;
|
|
1469
1556
|
- (void)syncScrollEdgeAppearance;
|
|
1470
1557
|
- (void)updateHardEdgeSeparatorCovers;
|
|
1558
|
+
- (void)updateTopOverlayThemeBackground;
|
|
1471
1559
|
- (void)updateScrollEdgeContentMasks;
|
|
1472
1560
|
- (void)restoreScrollEdgeContentMasks;
|
|
1473
1561
|
- (void)sharedHeaderPressCancellationGestureChanged:(UIPanGestureRecognizer *)recognizer;
|
|
@@ -1506,6 +1594,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1506
1594
|
UILabel *_stickyEdgeMarker;
|
|
1507
1595
|
CALayer *_topEdgeSeparatorCover;
|
|
1508
1596
|
CALayer *_stickyEdgeSeparatorCover;
|
|
1597
|
+
CALayer *_topOverlayThemeBackgroundLayer;
|
|
1509
1598
|
__weak UIView *_topEdgeInteractionHost;
|
|
1510
1599
|
__weak UIView *_stickyEdgeInteractionHost;
|
|
1511
1600
|
__weak UIScrollView *_topEdgeScrollView;
|
|
@@ -1535,6 +1624,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1535
1624
|
NSUInteger _pendingTopInsetReboundGeneration;
|
|
1536
1625
|
NSTimer *_topInsetReboundTimer;
|
|
1537
1626
|
CGFloat _nativeTabBarHeight;
|
|
1627
|
+
CGFloat _nativeTabBarTrailingReservedWidth;
|
|
1538
1628
|
CGFloat _nativeSubHeaderHeight;
|
|
1539
1629
|
CGFloat _headerOffset;
|
|
1540
1630
|
BOOL _scrollEnabled;
|
|
@@ -1576,6 +1666,13 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1576
1666
|
NSMapTable<UIScrollView *, NSValue *> *_originalInsets;
|
|
1577
1667
|
NSMapTable<UIScrollView *, UIRefreshControl *> *_managedRefreshControls;
|
|
1578
1668
|
NSHashTable<UIScrollView *> *_refreshControlObservedScrollViews;
|
|
1669
|
+
CFTimeInterval _homeVisualLastScrollLog;
|
|
1670
|
+
CFTimeInterval _homeVisualLastLayoutLog;
|
|
1671
|
+
CFTimeInterval _homeVisualStartMono;
|
|
1672
|
+
NSString *_homeVisualLastGeometrySignature;
|
|
1673
|
+
CGFloat _homeVisualLastThemeOpacity;
|
|
1674
|
+
CFTimeInterval _homeVisualLastThemeLog;
|
|
1675
|
+
BOOL _homeVisualThemeHolding;
|
|
1579
1676
|
NSMapTable<UIScrollView *, NSNumber *> *_appliedTopInsets;
|
|
1580
1677
|
NSMapTable<UIScrollView *, NSValue *> *_originalIndicatorInsets;
|
|
1581
1678
|
NSMapTable<UIScrollView *, NSNumber *> *_originalAlwaysBounceVertical;
|
|
@@ -1614,6 +1711,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1614
1711
|
RNCCollapsiblePagerOuterPagerPanGestureRecognizer *_sharedHeaderOuterPagerGesture;
|
|
1615
1712
|
RNCCollapsiblePagerVerticalPagerGuardGestureRecognizer *_verticalPagerGesture;
|
|
1616
1713
|
BOOL _observingContentOffset;
|
|
1714
|
+
BOOL _awaitingScrollOwnerForCurrentPage;
|
|
1715
|
+
BOOL _scrollReconcileScheduled;
|
|
1617
1716
|
BOOL _scrollResolveRetryScheduled;
|
|
1618
1717
|
CFTimeInterval _pendingFallbackScrollViewDetectedAt;
|
|
1619
1718
|
NSInteger _scrollResolveRetryPageIndex;
|
|
@@ -1645,6 +1744,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1645
1744
|
_displayBodyOverrides = [NSMutableDictionary new];
|
|
1646
1745
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(nativeListScrollCommandAccepted:)
|
|
1647
1746
|
name:@"OneKeyNativeListScrollCommandAccepted" object:nil];
|
|
1747
|
+
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(nativeScrollViewDidBecomeReadyNotification:)
|
|
1748
|
+
name:@"OneKeyNativeScrollViewDidBecomeReady" object:nil];
|
|
1648
1749
|
_pendingPageRestoreKeys = [NSMutableSet new];
|
|
1649
1750
|
_releasedPageScrollStates = [NSMutableDictionary new];
|
|
1650
1751
|
_mountingRemovedScrollViews = [NSHashTable weakObjectsHashTable];
|
|
@@ -1803,6 +1904,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1803
1904
|
[self updateTopScrollEdgeForScrollView:_observedScrollView];
|
|
1804
1905
|
[self updateSharedHeaderPressCancellationGesture];
|
|
1805
1906
|
[self updateHeaderSettledStateForceBusy:NO];
|
|
1907
|
+
[self logHomeVisualGeometry:@"window" scrollView:_observedScrollView];
|
|
1806
1908
|
for (UIScrollView *scrollView in _originalInsets.keyEnumerator) {
|
|
1807
1909
|
[self updateNativeScrollerViewport:scrollView stickyHeight:self.window == nil ? 0 : _stickyHeaderHeight + self.effectiveTopOverlayHeight];
|
|
1808
1910
|
}
|
|
@@ -1830,7 +1932,10 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1830
1932
|
|
|
1831
1933
|
- (CGFloat)effectiveTopOverlayHeight
|
|
1832
1934
|
{
|
|
1833
|
-
|
|
1935
|
+
BOOL localOverlayPending = _topScrollEdgeEnabled && _topOverlayHeight > 0 &&
|
|
1936
|
+
_externalTopOverlayTag <= 0;
|
|
1937
|
+
return _topOverlayView != nil || localOverlayPending
|
|
1938
|
+
? _topOverlayHeight : _externalTopOverlayHeight;
|
|
1834
1939
|
}
|
|
1835
1940
|
|
|
1836
1941
|
- (BOOL)hasFixedTopOverlay
|
|
@@ -1913,6 +2018,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1913
2018
|
[super traitCollectionDidChange:previousTraitCollection];
|
|
1914
2019
|
if (previousTraitCollection.userInterfaceStyle != self.traitCollection.userInterfaceStyle) {
|
|
1915
2020
|
[self syncScrollEdgeAppearance];
|
|
2021
|
+
[self updateTopOverlayThemeBackground];
|
|
2022
|
+
[self logHomeVisualGeometry:@"trait" scrollView:_observedScrollView];
|
|
1916
2023
|
}
|
|
1917
2024
|
}
|
|
1918
2025
|
|
|
@@ -1962,6 +2069,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1962
2069
|
self.window != nil && !_isBeingRecycled;
|
|
1963
2070
|
if (!enabled) {
|
|
1964
2071
|
[self restoreScrollEdgeContentMasks];
|
|
2072
|
+
[self updateTopOverlayThemeBackground];
|
|
1965
2073
|
return;
|
|
1966
2074
|
}
|
|
1967
2075
|
if (self.hasFixedTopOverlay && _sharedHeaderHostView.window == self.window &&
|
|
@@ -2031,6 +2139,102 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2031
2139
|
[_ownedPageMasks removeObjectForKey:page];
|
|
2032
2140
|
[_originalPageMasks removeObjectForKey:page];
|
|
2033
2141
|
}
|
|
2142
|
+
[self updateTopOverlayThemeBackground];
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
- (void)updateTopOverlayThemeBackground
|
|
2146
|
+
{
|
|
2147
|
+
if (@available(iOS 27.0, *)) {
|
|
2148
|
+
BOOL localHardOverlay = !_isBeingRecycled && _topScrollEdgeEnabled &&
|
|
2149
|
+
_topScrollEdgeEffect == RNCCollapsiblePagerViewTopScrollEdgeEffect::Hard &&
|
|
2150
|
+
_nativeSmoothHeaderScrollEnabled && _topOverlayView != nil &&
|
|
2151
|
+
_externalTopOverlayTag <= 0 && _topOverlayHeight > 0 && _headerHeight > 0 &&
|
|
2152
|
+
_topOverlayHostView.window == self.window && self.window != nil &&
|
|
2153
|
+
!CGRectIsEmpty(_topOverlayHostView.bounds);
|
|
2154
|
+
if (!localHardOverlay) {
|
|
2155
|
+
[_topOverlayThemeBackgroundLayer removeFromSuperlayer];
|
|
2156
|
+
_topOverlayThemeBackgroundLayer = nil;
|
|
2157
|
+
_homeVisualThemeHolding = NO;
|
|
2158
|
+
return;
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
UIColor *color = nil;
|
|
2162
|
+
for (UIView *view = self; view != nil; view = view.superview) {
|
|
2163
|
+
UIColor *candidate = [view.backgroundColor resolvedColorWithTraitCollection:
|
|
2164
|
+
_topOverlayHostView.traitCollection];
|
|
2165
|
+
if (candidate != nil && CGColorGetAlpha(candidate.CGColor) >= 1) {
|
|
2166
|
+
color = candidate;
|
|
2167
|
+
break;
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
if (color == nil) {
|
|
2171
|
+
[_topOverlayThemeBackgroundLayer removeFromSuperlayer];
|
|
2172
|
+
_topOverlayThemeBackgroundLayer = nil;
|
|
2173
|
+
return;
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
UIScrollView *scrollView = _observedScrollView;
|
|
2177
|
+
BOOL geometryReady = scrollView != nil &&
|
|
2178
|
+
_sharedHeaderScrollView == scrollView && _topEdgeScrollView == scrollView &&
|
|
2179
|
+
_topEdgeInteraction.scrollView == scrollView &&
|
|
2180
|
+
scrollView.topEdgeEffect.style == UIScrollEdgeEffectStyle.hardStyle &&
|
|
2181
|
+
_sharedHeaderMaskView != nil &&
|
|
2182
|
+
_sharedHeaderHostView.maskView == _sharedHeaderMaskView &&
|
|
2183
|
+
_headerView.window == self.window;
|
|
2184
|
+
if (_topOverlayThemeBackgroundLayer == nil && !geometryReady) return;
|
|
2185
|
+
BOOL created = _topOverlayThemeBackgroundLayer == nil;
|
|
2186
|
+
if (created) {
|
|
2187
|
+
_topOverlayThemeBackgroundLayer = [CALayer layer];
|
|
2188
|
+
_topOverlayThemeBackgroundLayer.name = @"onekey-top-overlay-theme-background";
|
|
2189
|
+
_topOverlayThemeBackgroundLayer.actions = @{
|
|
2190
|
+
@"position": NSNull.null, @"bounds": NSNull.null,
|
|
2191
|
+
@"opacity": NSNull.null, @"backgroundColor": NSNull.null
|
|
2192
|
+
};
|
|
2193
|
+
[_topOverlayHostView.layer insertSublayer:_topOverlayThemeBackgroundLayer atIndex:0];
|
|
2194
|
+
}
|
|
2195
|
+
_topOverlayThemeBackgroundLayer.frame = _topOverlayHostView.bounds;
|
|
2196
|
+
_topOverlayThemeBackgroundLayer.backgroundColor = color.CGColor;
|
|
2197
|
+
if (!geometryReady) {
|
|
2198
|
+
if (!_homeVisualThemeHolding) {
|
|
2199
|
+
_homeVisualThemeHolding = YES;
|
|
2200
|
+
#if DEBUG
|
|
2201
|
+
RNCCollapsiblePagerLog([NSString stringWithFormat:
|
|
2202
|
+
@"[IOS-HOME-VISUAL] phase=theme-background-hold pagerTag=%ld generation=%lu pageIndex=%ld opacity=%.3f",
|
|
2203
|
+
(long)self.tag, (unsigned long)_generation, (long)_currentIndex,
|
|
2204
|
+
_topOverlayThemeBackgroundLayer.opacity]);
|
|
2205
|
+
#endif
|
|
2206
|
+
}
|
|
2207
|
+
return;
|
|
2208
|
+
}
|
|
2209
|
+
_homeVisualThemeHolding = NO;
|
|
2210
|
+
CGRect overlayWindow = [_topOverlayHostView convertRect:_topOverlayHostView.bounds
|
|
2211
|
+
toView:self.window];
|
|
2212
|
+
CGRect headerWindow = [_headerView convertRect:_headerView.bounds
|
|
2213
|
+
toView:self.window];
|
|
2214
|
+
CGFloat overlayBottom = CGRectGetMaxY(overlayWindow);
|
|
2215
|
+
CGFloat collapse = MIN(MAX(overlayBottom - CGRectGetMinY(headerWindow), 0),
|
|
2216
|
+
_headerHeight);
|
|
2217
|
+
CGFloat opacity = 1 - collapse / _headerHeight;
|
|
2218
|
+
_topOverlayThemeBackgroundLayer.opacity = opacity;
|
|
2219
|
+
#if DEBUG
|
|
2220
|
+
CFTimeInterval now = CACurrentMediaTime();
|
|
2221
|
+
if (created || fabs(opacity - _homeVisualLastThemeOpacity) >= 0.1 ||
|
|
2222
|
+
now - _homeVisualLastThemeLog >= 2.0) {
|
|
2223
|
+
_homeVisualLastThemeOpacity = opacity;
|
|
2224
|
+
_homeVisualLastThemeLog = now;
|
|
2225
|
+
CGFloat red = 0, green = 0, blue = 0, alpha = 0;
|
|
2226
|
+
[color getRed:&red green:&green blue:&blue alpha:&alpha];
|
|
2227
|
+
RNCCollapsiblePagerLog([NSString stringWithFormat:
|
|
2228
|
+
@"[IOS-HOME-VISUAL] phase=theme-background epochMs=%.0f monoMs=%.0f pagerTag=%ld generation=%lu pageIndex=%ld opacity=%.3f collapse=%.1f header=%.1f overlayBottom=%.1f headerY=%.1f bodyYEstimate=%.1f hardOwner=%d rgba=%.3f,%.3f,%.3f,%.3f",
|
|
2229
|
+
NSDate.date.timeIntervalSince1970 * 1000, now * 1000,
|
|
2230
|
+
(long)self.tag, (unsigned long)_generation, (long)_currentIndex,
|
|
2231
|
+
opacity, collapse, (CGFloat)_headerHeight, overlayBottom,
|
|
2232
|
+
CGRectGetMinY(headerWindow), overlayBottom + _headerHeight +
|
|
2233
|
+
_stickyHeaderHeight - collapse, _topEdgeInteraction.scrollView == scrollView,
|
|
2234
|
+
red, green, blue, alpha]);
|
|
2235
|
+
}
|
|
2236
|
+
#endif
|
|
2237
|
+
}
|
|
2034
2238
|
}
|
|
2035
2239
|
|
|
2036
2240
|
- (void)updateEventEmitter:(const EventEmitter::Shared &)eventEmitter
|
|
@@ -2187,6 +2391,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2187
2391
|
CGRectGetWidth(_containerView.bounds), pixel);
|
|
2188
2392
|
cover.contentsScale = scale;
|
|
2189
2393
|
cover.backgroundColor = color.CGColor;
|
|
2394
|
+
cover.hidden = index == 0 && _stickyHeaderIsPinned;
|
|
2190
2395
|
}
|
|
2191
2396
|
if (index == 0) _topEdgeSeparatorCover = cover;
|
|
2192
2397
|
else _stickyEdgeSeparatorCover = cover;
|
|
@@ -2198,6 +2403,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2198
2403
|
[self updateTopScrollEdgeForScrollView:nil];
|
|
2199
2404
|
[self cancelHeaderSettledCheck];
|
|
2200
2405
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"OneKeyNativeListScrollCommandAccepted" object:nil];
|
|
2406
|
+
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"OneKeyNativeScrollViewDidBecomeReady" object:nil];
|
|
2201
2407
|
for (UIScrollView *scrollView in _originalInsets.keyEnumerator) {
|
|
2202
2408
|
[self updateNativeScrollerViewport:scrollView stickyHeight:0];
|
|
2203
2409
|
}
|
|
@@ -2345,6 +2551,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2345
2551
|
}
|
|
2346
2552
|
[_nativeTabBarView updateAncestorGesturePrecedence];
|
|
2347
2553
|
[_nativeSubHeaderView updateAncestorGesturePrecedence];
|
|
2554
|
+
[self bringReservedStickyControlToFront];
|
|
2348
2555
|
}
|
|
2349
2556
|
|
|
2350
2557
|
- (void)restoreSharedHeadersToContainer
|
|
@@ -2450,10 +2657,11 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2450
2657
|
CGFloat nativeTabBarVisibleHeight = _nativeTabBarView.hidden
|
|
2451
2658
|
? 0
|
|
2452
2659
|
: MIN(_stickyHeaderHeight, _nativeTabBarHeight);
|
|
2660
|
+
CGFloat tabReservedWidth = MIN(width, MAX(0, _nativeTabBarTrailingReservedWidth));
|
|
2453
2661
|
_nativeTabBarView.frame = CGRectMake(
|
|
2454
|
-
0,
|
|
2662
|
+
self.isLtrLayout ? 0 : tabReservedWidth,
|
|
2455
2663
|
stickyChildY,
|
|
2456
|
-
width,
|
|
2664
|
+
width - tabReservedWidth,
|
|
2457
2665
|
nativeTabBarVisibleHeight
|
|
2458
2666
|
);
|
|
2459
2667
|
_nativeSubHeaderView.frame = CGRectMake(
|
|
@@ -2467,9 +2675,26 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2467
2675
|
);
|
|
2468
2676
|
_topOverlayHostView.frame = CGRectMake(0, 0, width, self.effectiveTopOverlayHeight);
|
|
2469
2677
|
_topOverlayView.frame = _topOverlayHostView.bounds;
|
|
2678
|
+
[self bringReservedStickyControlToFront];
|
|
2470
2679
|
[self applyHeaderOffset];
|
|
2471
2680
|
}
|
|
2472
2681
|
|
|
2682
|
+
- (void)bringReservedStickyControlToFront
|
|
2683
|
+
{
|
|
2684
|
+
if (_stickyHeaderView != nil &&
|
|
2685
|
+
_stickyHeaderView.superview == _nativeTabBarView.superview) {
|
|
2686
|
+
if (_nativeTabBarTrailingReservedWidth > 0) {
|
|
2687
|
+
[_stickyHeaderView.superview bringSubviewToFront:_stickyHeaderView];
|
|
2688
|
+
} else {
|
|
2689
|
+
[_nativeTabBarView.superview bringSubviewToFront:_nativeTabBarView];
|
|
2690
|
+
if (!_nativeSubHeaderView.hidden &&
|
|
2691
|
+
_nativeSubHeaderView.superview == _nativeTabBarView.superview) {
|
|
2692
|
+
[_nativeSubHeaderView.superview bringSubviewToFront:_nativeSubHeaderView];
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
|
|
2473
2698
|
- (void)updatePinnedStickyHeader
|
|
2474
2699
|
{
|
|
2475
2700
|
BOOL attachedToList = _nativeSmoothHeaderScrollEnabled &&
|
|
@@ -2516,8 +2741,10 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2516
2741
|
CGFloat childY = shouldPin ? 0 : _headerHeight;
|
|
2517
2742
|
CGFloat width = CGRectGetWidth(self.bounds);
|
|
2518
2743
|
CGFloat tabHeight = _nativeTabBarView.hidden ? 0 : MIN(_stickyHeaderHeight, _nativeTabBarHeight);
|
|
2744
|
+
CGFloat tabReservedWidth = MIN(width, MAX(0, _nativeTabBarTrailingReservedWidth));
|
|
2519
2745
|
_stickyHeaderView.frame = CGRectMake(0, childY, width, _stickyHeaderHeight);
|
|
2520
|
-
_nativeTabBarView.frame = CGRectMake(0
|
|
2746
|
+
_nativeTabBarView.frame = CGRectMake(self.isLtrLayout ? 0 : tabReservedWidth,
|
|
2747
|
+
childY, width - tabReservedWidth, tabHeight);
|
|
2521
2748
|
_nativeSubHeaderView.frame = CGRectMake(0, childY + tabHeight, width,
|
|
2522
2749
|
MIN(MAX(0, _stickyHeaderHeight - tabHeight), _nativeSubHeaderHeight));
|
|
2523
2750
|
_pinnedStickyHostView.hidden = !shouldPin;
|
|
@@ -2537,7 +2764,10 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2537
2764
|
}
|
|
2538
2765
|
[_nativeTabBarView updateAncestorGesturePrecedence];
|
|
2539
2766
|
[_nativeSubHeaderView updateAncestorGesturePrecedence];
|
|
2767
|
+
[self bringReservedStickyControlToFront];
|
|
2540
2768
|
[self updateTopScrollEdgeForScrollView:_observedScrollView];
|
|
2769
|
+
[self logHomeVisualGeometry:shouldPin ? @"pin" : @"unpin"
|
|
2770
|
+
scrollView:_observedScrollView];
|
|
2541
2771
|
}
|
|
2542
2772
|
|
|
2543
2773
|
- (void)preparePageForHorizontalTransitionAtIndex:(NSInteger)pageIndex
|
|
@@ -2689,6 +2919,15 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2689
2919
|
- (void)mountingTransactionDidMount:(const MountingTransaction &)transaction
|
|
2690
2920
|
withSurfaceTelemetry:(const SurfaceTelemetry &)surfaceTelemetry
|
|
2691
2921
|
{
|
|
2922
|
+
if (_needsSlotRebuild && _deferTopEdgeSlotRebuild &&
|
|
2923
|
+
_topScrollEdgeEnabled && _nativeSmoothHeaderScrollEnabled &&
|
|
2924
|
+
_topScrollEdgeEffect == RNCCollapsiblePagerViewTopScrollEdgeEffect::Hard &&
|
|
2925
|
+
_topOverlayHeight > 0 && _pageKeys.count > 0 &&
|
|
2926
|
+
_externalTopOverlayTag <= 0 &&
|
|
2927
|
+
_logicalChildren.count == _pageKeys.count + 3 &&
|
|
2928
|
+
!_transitioning && !_isPagerDragging && !_isBeingRecycled) {
|
|
2929
|
+
_deferTopEdgeSlotRebuild = NO;
|
|
2930
|
+
}
|
|
2692
2931
|
if (_needsSlotRebuild && !_deferTopEdgeSlotRebuild &&
|
|
2693
2932
|
!_transitioning && !_isPagerDragging) {
|
|
2694
2933
|
[self rebuildSlots];
|
|
@@ -2711,6 +2950,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2711
2950
|
}
|
|
2712
2951
|
[self updateScrollEdgeContentMasks];
|
|
2713
2952
|
[_mountingRemovedScrollViews removeAllObjects];
|
|
2953
|
+
[self logHomeVisualGeometry:@"did-mount" scrollView:_observedScrollView];
|
|
2714
2954
|
if (!_nativeSmoothHeaderScrollEnabled || _isBeingRecycled || self.window == nil ||
|
|
2715
2955
|
_currentIndex < 0 || _currentIndex >= _pageControllers.count) return;
|
|
2716
2956
|
// Prepare newly mounted owned scrollers before their first frame, even while
|
|
@@ -2728,6 +2968,23 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2728
2968
|
[self applyInsetsToScrollView:scrollView pageIndex:index restore:YES];
|
|
2729
2969
|
}
|
|
2730
2970
|
}
|
|
2971
|
+
UIView *page = _pageControllers[_currentIndex].view;
|
|
2972
|
+
if (_scrollResolveRetryAttempt >= 12 &&
|
|
2973
|
+
_releasedPageScrollStates[self.currentPageKey] != nil) {
|
|
2974
|
+
for (const auto &mutation : transaction.getMutations()) {
|
|
2975
|
+
if (mutation.type == ShadowViewMutation::Insert && mutation.parentTag > 0 &&
|
|
2976
|
+
[page viewWithTag:mutation.parentTag] != nil) {
|
|
2977
|
+
// A new current-page insertion starts a bounded recovery round after
|
|
2978
|
+
// the previous replacement search has scheduled its final retry.
|
|
2979
|
+
_scrollResolveRetryToken += 1;
|
|
2980
|
+
_scrollResolveRetryScheduled = NO;
|
|
2981
|
+
_scrollResolveRetryAttempt = 0;
|
|
2982
|
+
break;
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
|
|
2987
|
+
// Record recovery during transitions; ownership still waits for settlement.
|
|
2731
2988
|
if (_transitioning || _isPagerDragging) return;
|
|
2732
2989
|
|
|
2733
2990
|
// OneKey patch: a release must recover even when the changed parent is above
|
|
@@ -2736,7 +2993,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2736
2993
|
[self attachScrollObserverForCurrentPage];
|
|
2737
2994
|
return;
|
|
2738
2995
|
}
|
|
2739
|
-
UIView *page = _pageControllers[_currentIndex].view;
|
|
2740
2996
|
BOOL headerIsAttached = [_sharedHeaderHostView isDescendantOfView:self];
|
|
2741
2997
|
BOOL currentListIsAttached = _observedScrollView != nil &&
|
|
2742
2998
|
[_observedScrollView isDescendantOfView:page] && headerIsAttached;
|
|
@@ -2904,18 +3160,14 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2904
3160
|
if (_stickyHeaderView != nil) [_sharedHeaderHostView bringSubviewToFront:_stickyHeaderView];
|
|
2905
3161
|
if (!_nativeTabBarView.hidden) [_sharedHeaderHostView bringSubviewToFront:_nativeTabBarView];
|
|
2906
3162
|
if (!_nativeSubHeaderView.hidden) [_sharedHeaderHostView bringSubviewToFront:_nativeSubHeaderView];
|
|
3163
|
+
[self bringReservedStickyControlToFront];
|
|
2907
3164
|
[_containerView bringSubviewToFront:_sharedHeaderHostView];
|
|
2908
3165
|
if (_stickyHeaderIsPinned) {
|
|
2909
3166
|
[_pinnedStickyHostView.superview bringSubviewToFront:_pinnedStickyHostView];
|
|
2910
3167
|
}
|
|
2911
3168
|
if (self.hasFixedTopOverlay) [_containerView bringSubviewToFront:_topOverlayHostView];
|
|
2912
3169
|
[self setNeedsLayout];
|
|
2913
|
-
|
|
2914
|
-
if (!self->_isBeingRecycled) {
|
|
2915
|
-
[self prepareAdjacentPageInsets];
|
|
2916
|
-
[self attachScrollObserverForCurrentPage];
|
|
2917
|
-
}
|
|
2918
|
-
});
|
|
3170
|
+
[self scheduleCoalescedScrollReconcile];
|
|
2919
3171
|
}
|
|
2920
3172
|
|
|
2921
3173
|
- (void)layoutSubviews
|
|
@@ -2991,12 +3243,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2991
3243
|
[self updateTopScrollEdgeForScrollView:_observedScrollView];
|
|
2992
3244
|
[self updateScrollEdgeContentMasks];
|
|
2993
3245
|
[self updateHeaderSettledStateForceBusy:NO];
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
[self prepareAdjacentPageInsets];
|
|
2997
|
-
[self attachScrollObserverForCurrentPage];
|
|
2998
|
-
}
|
|
2999
|
-
});
|
|
3246
|
+
[self logHomeVisualGeometry:@"layout" scrollView:_observedScrollView];
|
|
3247
|
+
[self scheduleCoalescedScrollReconcile];
|
|
3000
3248
|
}
|
|
3001
3249
|
|
|
3002
3250
|
- (void)prepareForRecycle
|
|
@@ -3014,6 +3262,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3014
3262
|
_generation++;
|
|
3015
3263
|
[_mountingRemovedScrollViews removeAllObjects];
|
|
3016
3264
|
_needsScrollObserverReattachAfterMount = NO;
|
|
3265
|
+
_awaitingScrollOwnerForCurrentPage = NO;
|
|
3266
|
+
_scrollReconcileScheduled = NO;
|
|
3017
3267
|
[_releasedPageScrollStates removeAllObjects];
|
|
3018
3268
|
[self detachSharedHeaderPressCancellationGesture];
|
|
3019
3269
|
[self detachScrollObserver];
|
|
@@ -3110,7 +3360,12 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3110
3360
|
_externalOuterTransitioning = NO;
|
|
3111
3361
|
_topScrollEdgeEnabled = NO;
|
|
3112
3362
|
_topScrollEdgeEffect = RNCCollapsiblePagerViewTopScrollEdgeEffect::None;
|
|
3363
|
+
_homeVisualStartMono = 0;
|
|
3364
|
+
_homeVisualLastScrollLog = 0;
|
|
3365
|
+
_homeVisualLastLayoutLog = 0;
|
|
3366
|
+
_homeVisualLastGeometrySignature = nil;
|
|
3113
3367
|
_nativeTabBarHeight = 44;
|
|
3368
|
+
_nativeTabBarTrailingReservedWidth = 0;
|
|
3114
3369
|
_nativeSubHeaderHeight = 74;
|
|
3115
3370
|
_headerOffset = 0;
|
|
3116
3371
|
_currentLogicalOffset = 0;
|
|
@@ -3192,6 +3447,13 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3192
3447
|
_layoutDirection = RCTNSStringFromString(toString(newViewProps.layoutDirection));
|
|
3193
3448
|
[_nativeTabBarView setLeftToRight:self.isLtrLayout];
|
|
3194
3449
|
[_nativeSubHeaderView setLeftToRight:self.isLtrLayout];
|
|
3450
|
+
[self setNeedsLayout];
|
|
3451
|
+
}
|
|
3452
|
+
if (_needsPropsReapply ||
|
|
3453
|
+
oldViewProps.nativeTabBarTrailingReservedWidth != newViewProps.nativeTabBarTrailingReservedWidth) {
|
|
3454
|
+
double reserved = newViewProps.nativeTabBarTrailingReservedWidth;
|
|
3455
|
+
_nativeTabBarTrailingReservedWidth = std::isfinite(reserved) && reserved >= 0 ? reserved : 0;
|
|
3456
|
+
[self setNeedsLayout];
|
|
3195
3457
|
}
|
|
3196
3458
|
BOOL insetsChanged = NO;
|
|
3197
3459
|
if (_needsPropsReapply || oldViewProps.headerHeight != newViewProps.headerHeight) {
|
|
@@ -3272,7 +3534,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3272
3534
|
}
|
|
3273
3535
|
if (_needsPropsReapply ||
|
|
3274
3536
|
oldViewProps.nativeTabBarHeight != newViewProps.nativeTabBarHeight ||
|
|
3537
|
+
oldViewProps.nativeTabBarVariant != newViewProps.nativeTabBarVariant ||
|
|
3275
3538
|
oldViewProps.nativeTabBarContentPaddingHorizontal != newViewProps.nativeTabBarContentPaddingHorizontal ||
|
|
3539
|
+
oldViewProps.nativeTabBarContentPaddingVertical != newViewProps.nativeTabBarContentPaddingVertical ||
|
|
3276
3540
|
oldViewProps.nativeTabBarItemSpacing != newViewProps.nativeTabBarItemSpacing ||
|
|
3277
3541
|
oldViewProps.nativeTabBarFontSize != newViewProps.nativeTabBarFontSize ||
|
|
3278
3542
|
oldViewProps.nativeTabBarFontFamily != newViewProps.nativeTabBarFontFamily ||
|
|
@@ -3289,7 +3553,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3289
3553
|
NSString *fontFamily = RCTNSStringFromString(newViewProps.nativeTabBarFontFamily);
|
|
3290
3554
|
[_nativeTabBarView
|
|
3291
3555
|
updateStyleWithHeight:_nativeTabBarHeight
|
|
3556
|
+
variant:RCTNSStringFromString(newViewProps.nativeTabBarVariant)
|
|
3292
3557
|
contentPaddingHorizontal:MAX(0, newViewProps.nativeTabBarContentPaddingHorizontal)
|
|
3558
|
+
contentPaddingVertical:MAX(0, newViewProps.nativeTabBarContentPaddingVertical)
|
|
3293
3559
|
itemSpacing:MAX(0, newViewProps.nativeTabBarItemSpacing)
|
|
3294
3560
|
fontSize:newViewProps.nativeTabBarFontSize > 0 ? newViewProps.nativeTabBarFontSize : 16
|
|
3295
3561
|
fontFamily:fontFamily
|
|
@@ -3314,6 +3580,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3314
3580
|
if (insetsChanged) {
|
|
3315
3581
|
[self reapplyInsetsToObservedScrollView];
|
|
3316
3582
|
[self setNeedsLayout];
|
|
3583
|
+
[self logHomeVisualGeometry:@"props-inset" scrollView:_observedScrollView];
|
|
3317
3584
|
}
|
|
3318
3585
|
_applyingTopOverlayInsetChange = NO;
|
|
3319
3586
|
if (!_nativeTabBarView.hidden) {
|
|
@@ -3322,6 +3589,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3322
3589
|
if (!_nativeSubHeaderView.hidden) {
|
|
3323
3590
|
[_nativeSubHeaderView.superview bringSubviewToFront:_nativeSubHeaderView];
|
|
3324
3591
|
}
|
|
3592
|
+
[self bringReservedStickyControlToFront];
|
|
3325
3593
|
[_sharedHeaderHostView.superview bringSubviewToFront:_sharedHeaderHostView];
|
|
3326
3594
|
if (_stickyHeaderIsPinned) {
|
|
3327
3595
|
[_pinnedStickyHostView.superview bringSubviewToFront:_pinnedStickyHostView];
|
|
@@ -3349,12 +3617,107 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3349
3617
|
|
|
3350
3618
|
- (UIScrollView *)verticalScrollViewForPageAtIndex:(NSInteger)pageIndex
|
|
3351
3619
|
{
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3620
|
+
return [self resolveScrollCandidateForPageAtIndex:pageIndex].scrollView;
|
|
3621
|
+
}
|
|
3622
|
+
|
|
3623
|
+
- (RNCScrollCandidateResolution)resolveScrollCandidateForPageAtIndex:(NSInteger)pageIndex
|
|
3624
|
+
{
|
|
3625
|
+
if (pageIndex < 0 || pageIndex >= _pageControllers.count) return {};
|
|
3626
|
+
return [self resolveScrollCandidateInView:_pageControllers[pageIndex].view];
|
|
3627
|
+
}
|
|
3628
|
+
|
|
3629
|
+
// Single-pass tiered resolution replaces three separate subtree walks while
|
|
3630
|
+
// distinguishing genuinely static pages from pages with unready scroll hosts.
|
|
3631
|
+
- (RNCScrollCandidateResolution)resolveScrollCandidateInView:(UIView *)view
|
|
3632
|
+
{
|
|
3633
|
+
RNCScrollCandidateResolution resolution;
|
|
3634
|
+
if (view == nil || view == _sharedHeaderHostView) return resolution;
|
|
3635
|
+
if (view != self &&
|
|
3636
|
+
[view isKindOfClass:RNCCollapsiblePagerViewComponentView.class]) {
|
|
3637
|
+
UIScrollView *nested = [(RNCCollapsiblePagerViewComponentView *)view
|
|
3638
|
+
currentVerticalScrollViewForParentPager];
|
|
3639
|
+
if (nested != nil) {
|
|
3640
|
+
resolution.scrollView = nested;
|
|
3641
|
+
resolution.tier = RNCScrollCandidateTierExplicitScroller;
|
|
3642
|
+
} else {
|
|
3643
|
+
resolution.hasUnreadyScrollHost = YES;
|
|
3644
|
+
}
|
|
3645
|
+
return resolution;
|
|
3646
|
+
}
|
|
3647
|
+
|
|
3648
|
+
Class nativeListClass = NSClassFromString(@"HybridNativeListComponent");
|
|
3649
|
+
if (nativeListClass != Nil && [view isKindOfClass:nativeListClass]) {
|
|
3650
|
+
resolution.hasUnreadyScrollHost = YES;
|
|
3651
|
+
}
|
|
3652
|
+
|
|
3653
|
+
if ([view isKindOfClass:RNCNativeScrollerComponentView.class]) {
|
|
3654
|
+
UIScrollView *scrollView = ((RNCNativeScrollerComponentView *)view).scrollView;
|
|
3655
|
+
if (![_mountingRemovedScrollViews containsObject:scrollView]) {
|
|
3656
|
+
resolution.scrollView = scrollView;
|
|
3657
|
+
resolution.tier = RNCScrollCandidateTierExplicitScroller;
|
|
3658
|
+
return resolution;
|
|
3659
|
+
}
|
|
3660
|
+
} else if ([view isKindOfClass:RCTScrollViewComponentView.class]) {
|
|
3661
|
+
RCTScrollViewComponentView *componentView = (RCTScrollViewComponentView *)view;
|
|
3662
|
+
UIScrollView *scrollView = componentView.scrollView;
|
|
3663
|
+
if (![_mountingRemovedScrollViews containsObject:scrollView]) {
|
|
3664
|
+
if ([componentView.nativeId hasPrefix:RNCCollapsiblePagerNativeScrollerIDPrefix]) {
|
|
3665
|
+
resolution.scrollView = scrollView;
|
|
3666
|
+
resolution.tier = RNCScrollCandidateTierExplicitScroller;
|
|
3667
|
+
return resolution;
|
|
3668
|
+
}
|
|
3669
|
+
if (!scrollView.alwaysBounceHorizontal) {
|
|
3670
|
+
resolution.scrollView = scrollView;
|
|
3671
|
+
resolution.tier = RNCScrollCandidateTierFabricScrollView;
|
|
3672
|
+
}
|
|
3673
|
+
}
|
|
3674
|
+
} else if ([view isKindOfClass:UICollectionView.class]) {
|
|
3675
|
+
UICollectionView *collectionView = (UICollectionView *)view;
|
|
3676
|
+
if (![_mountingRemovedScrollViews containsObject:collectionView]) {
|
|
3677
|
+
UICollectionViewLayout *layout = collectionView.collectionViewLayout;
|
|
3678
|
+
BOOL isVerticalLayout = ![layout isKindOfClass:UICollectionViewFlowLayout.class] ||
|
|
3679
|
+
((UICollectionViewFlowLayout *)layout).scrollDirection == UICollectionViewScrollDirectionVertical;
|
|
3680
|
+
if (isVerticalLayout) {
|
|
3681
|
+
BOOL hasNativeListHost = RNCNativeListComponentForScrollView(collectionView) != nil;
|
|
3682
|
+
BOOL isMountedNativeList = hasNativeListHost && RNCIsMountedNativeListScrollView(collectionView);
|
|
3683
|
+
BOOL hasUsableBounds = CGRectGetWidth(collectionView.bounds) > 0 &&
|
|
3684
|
+
CGRectGetHeight(collectionView.bounds) > 0;
|
|
3685
|
+
if (hasNativeListHost && !isMountedNativeList) {
|
|
3686
|
+
resolution.hasUnreadyScrollHost = YES;
|
|
3687
|
+
} else if (!hasUsableBounds) {
|
|
3688
|
+
resolution.hasUnreadyScrollHost = YES;
|
|
3689
|
+
} else {
|
|
3690
|
+
resolution.scrollView = collectionView;
|
|
3691
|
+
resolution.tier = isMountedNativeList
|
|
3692
|
+
? RNCScrollCandidateTierVerifiedNativeList
|
|
3693
|
+
: RNCScrollCandidateTierGenericFallback;
|
|
3694
|
+
// React slots inside a verified list may own independent scroll views.
|
|
3695
|
+
if (isMountedNativeList) return resolution;
|
|
3696
|
+
}
|
|
3697
|
+
}
|
|
3698
|
+
}
|
|
3699
|
+
} else if ([view isKindOfClass:UIScrollView.class]) {
|
|
3700
|
+
UIScrollView *scrollView = (UIScrollView *)view;
|
|
3701
|
+
if (![_mountingRemovedScrollViews containsObject:scrollView] &&
|
|
3702
|
+
!scrollView.alwaysBounceHorizontal &&
|
|
3703
|
+
scrollView.contentSize.width <= scrollView.bounds.size.width + 1) {
|
|
3704
|
+
resolution.scrollView = scrollView;
|
|
3705
|
+
resolution.tier = RNCScrollCandidateTierGenericFallback;
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
|
|
3709
|
+
for (UIView *subview in view.subviews) {
|
|
3710
|
+
RNCScrollCandidateResolution child = [self resolveScrollCandidateInView:subview];
|
|
3711
|
+
resolution.hasUnreadyScrollHost |= child.hasUnreadyScrollHost;
|
|
3712
|
+
if (child.tier > resolution.tier) {
|
|
3713
|
+
resolution.scrollView = child.scrollView;
|
|
3714
|
+
resolution.tier = child.tier;
|
|
3715
|
+
if (resolution.tier == RNCScrollCandidateTierExplicitScroller) {
|
|
3716
|
+
return resolution;
|
|
3717
|
+
}
|
|
3718
|
+
}
|
|
3719
|
+
}
|
|
3720
|
+
return resolution;
|
|
3358
3721
|
}
|
|
3359
3722
|
|
|
3360
3723
|
- (UIScrollView *)findExplicitNativeScrollerInView:(UIView *)view
|
|
@@ -3450,6 +3813,19 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3450
3813
|
return nil;
|
|
3451
3814
|
}
|
|
3452
3815
|
|
|
3816
|
+
- (void)scheduleCoalescedScrollReconcile
|
|
3817
|
+
{
|
|
3818
|
+
if (_isBeingRecycled || _scrollReconcileScheduled) return;
|
|
3819
|
+
_scrollReconcileScheduled = YES;
|
|
3820
|
+
NSUInteger generation = _generation;
|
|
3821
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
3822
|
+
self->_scrollReconcileScheduled = NO;
|
|
3823
|
+
if (self->_isBeingRecycled || self->_generation != generation) return;
|
|
3824
|
+
[self prepareAdjacentPageInsets];
|
|
3825
|
+
[self attachScrollObserverForCurrentPage];
|
|
3826
|
+
});
|
|
3827
|
+
}
|
|
3828
|
+
|
|
3453
3829
|
- (void)scheduleScrollObserverRetryForCurrentPage
|
|
3454
3830
|
{
|
|
3455
3831
|
if (_scrollResolveRetryScheduled || _scrollResolveRetryAttempt >= 12 ||
|
|
@@ -3502,29 +3878,35 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3502
3878
|
_pendingFallbackScrollView = nil;
|
|
3503
3879
|
_pendingFallbackScrollViewDetectedAt = 0;
|
|
3504
3880
|
}
|
|
3505
|
-
|
|
3881
|
+
RNCScrollCandidateResolution resolution = [self resolveScrollCandidateForPageAtIndex:_currentIndex];
|
|
3882
|
+
UIScrollView *candidate = resolution.scrollView;
|
|
3506
3883
|
if (_nativeSmoothHeaderScrollEnabled && _currentIndex >= 0 && _currentIndex < _pageControllers.count) {
|
|
3507
3884
|
RNCGuardNestedHorizontalPans(_pageControllers[_currentIndex].view, _verticalPagerGesture);
|
|
3508
3885
|
}
|
|
3509
3886
|
if (candidate == nil) {
|
|
3510
3887
|
[self detachScrollObserver];
|
|
3511
3888
|
[self restoreSharedHeadersToContainer];
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3889
|
+
_needsScrollObserverReattachAfterMount = NO;
|
|
3890
|
+
_pendingFallbackScrollView = nil;
|
|
3891
|
+
_pendingFallbackScrollViewDetectedAt = 0;
|
|
3892
|
+
_awaitingScrollOwnerForCurrentPage = YES;
|
|
3893
|
+
// Only schedule bounded retry when the page has an unready scroll host in
|
|
3894
|
+
// its subtree or a released scroll owner awaiting replacement. Static pages
|
|
3895
|
+
// without a scroll host do not spin retry timers, and late replacements on
|
|
3896
|
+
// any page also attach deterministically via readiness notifications.
|
|
3897
|
+
BOOL hasReleasedOwnerAwaitingReplacement = _releasedPageScrollStates[self.currentPageKey] != nil;
|
|
3898
|
+
if (resolution.hasUnreadyScrollHost || hasReleasedOwnerAwaitingReplacement) {
|
|
3899
|
+
[self scheduleScrollObserverRetryForCurrentPage];
|
|
3900
|
+
} else {
|
|
3516
3901
|
_scrollResolveRetryToken += 1;
|
|
3517
3902
|
_scrollResolveRetryScheduled = NO;
|
|
3518
3903
|
_scrollResolveRetryAttempt = 0;
|
|
3519
|
-
_pendingFallbackScrollView = nil;
|
|
3520
|
-
_pendingFallbackScrollViewDetectedAt = 0;
|
|
3521
|
-
} else {
|
|
3522
|
-
[self scheduleScrollObserverRetryForCurrentPage];
|
|
3523
3904
|
}
|
|
3524
3905
|
return;
|
|
3525
3906
|
}
|
|
3526
3907
|
if ([candidate isKindOfClass:UICollectionView.class] &&
|
|
3527
3908
|
candidate != _observedScrollView && !RNCIsMountedNativeListScrollView(candidate)) {
|
|
3909
|
+
_awaitingScrollOwnerForCurrentPage = YES;
|
|
3528
3910
|
if (_pendingFallbackScrollView != candidate) {
|
|
3529
3911
|
_pendingFallbackScrollView = candidate;
|
|
3530
3912
|
_pendingFallbackScrollViewDetectedAt = CACurrentMediaTime();
|
|
@@ -3536,6 +3918,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3536
3918
|
return;
|
|
3537
3919
|
}
|
|
3538
3920
|
}
|
|
3921
|
+
_awaitingScrollOwnerForCurrentPage = NO;
|
|
3539
3922
|
_scrollResolveRetryAttempt = 0;
|
|
3540
3923
|
_scrollResolveRetryPageIndex = _currentIndex;
|
|
3541
3924
|
_scrollResolveRetryToken += 1;
|
|
@@ -3894,8 +4277,105 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3894
4277
|
[_managedRefreshControls removeObjectForKey:scrollView];
|
|
3895
4278
|
if (current != nil) [_managedRefreshControls setObject:current forKey:scrollView];
|
|
3896
4279
|
}
|
|
3897
|
-
[RNCRefreshControlOffsetState setOffset:
|
|
4280
|
+
[RNCRefreshControlOffsetState setOffset:_headerHeight + _stickyHeaderHeight
|
|
3898
4281
|
forControl:current owner:self];
|
|
4282
|
+
if (previous != current) [self logHomeVisualGeometry:@"refresh-bind" scrollView:scrollView];
|
|
4283
|
+
}
|
|
4284
|
+
|
|
4285
|
+
- (void)logHomeVisualGeometry:(NSString *)phase scrollView:(UIScrollView *)scrollView
|
|
4286
|
+
{
|
|
4287
|
+
#if DEBUG
|
|
4288
|
+
if (self.window == nil || _isBeingRecycled) return;
|
|
4289
|
+
CFTimeInterval now = CACurrentMediaTime();
|
|
4290
|
+
if (_homeVisualStartMono == 0) _homeVisualStartMono = now;
|
|
4291
|
+
if ([phase isEqualToString:@"pull"] && now - _homeVisualLastScrollLog < 0.2) return;
|
|
4292
|
+
if ([phase isEqualToString:@"pull"]) _homeVisualLastScrollLog = now;
|
|
4293
|
+
UIRefreshControl *control = scrollView.refreshControl;
|
|
4294
|
+
CGRect controlWindow = control != nil
|
|
4295
|
+
? [control convertRect:control.bounds toView:self.window] : CGRectZero;
|
|
4296
|
+
CGRect overlayWindow = [_topOverlayHostView convertRect:_topOverlayHostView.bounds
|
|
4297
|
+
toView:self.window];
|
|
4298
|
+
CGRect stickyWindow = [_stickyHeaderView convertRect:_stickyHeaderView.bounds
|
|
4299
|
+
toView:self.window];
|
|
4300
|
+
CGRect maskWindow = _sharedHeaderMaskView != nil
|
|
4301
|
+
? [_sharedHeaderHostView convertRect:_sharedHeaderMaskView.frame toView:self.window]
|
|
4302
|
+
: CGRectZero;
|
|
4303
|
+
CGFloat pageMaskWindowY = [_containerView convertPoint:
|
|
4304
|
+
CGPointMake(0, _pageMaskBoundary) toView:self.window].y;
|
|
4305
|
+
BOOL layoutPhase = [phase isEqualToString:@"layout"] ||
|
|
4306
|
+
[phase isEqualToString:@"did-mount"];
|
|
4307
|
+
NSString *signature = [NSString stringWithFormat:
|
|
4308
|
+
@"%.1f:%ld:%ld:%.1f:%d:%.1f:%.1f:%.1f:%d:%d",
|
|
4309
|
+
self.effectiveTopOverlayHeight, (long)_headerHeight,
|
|
4310
|
+
(long)_stickyHeaderHeight, scrollView.contentInset.top, control != nil,
|
|
4311
|
+
CGRectGetMinY(maskWindow), CGRectGetHeight(maskWindow), _pageMaskBoundary,
|
|
4312
|
+
_stickyHeaderIsPinned, _topScrollEdgeEnabled];
|
|
4313
|
+
if (layoutPhase && now - _homeVisualStartMono >= 2.0 &&
|
|
4314
|
+
[_homeVisualLastGeometrySignature isEqualToString:signature] &&
|
|
4315
|
+
now - _homeVisualLastLayoutLog < 2.0) return;
|
|
4316
|
+
if (layoutPhase) {
|
|
4317
|
+
_homeVisualLastLayoutLog = now;
|
|
4318
|
+
_homeVisualLastGeometrySignature = signature;
|
|
4319
|
+
}
|
|
4320
|
+
CGFloat visualYEstimate = CGRectGetMinY(controlWindow) +
|
|
4321
|
+
(control != nil ? control.layer.sublayerTransform.m42 : 0);
|
|
4322
|
+
CALayer *firstChild = control.layer.sublayers.firstObject;
|
|
4323
|
+
BOOL childAttached = firstChild != nil && control.window == self.window;
|
|
4324
|
+
CGRect firstChildWindow = childAttached
|
|
4325
|
+
? [firstChild convertRect:firstChild.bounds toLayer:self.window.layer]
|
|
4326
|
+
: CGRectZero;
|
|
4327
|
+
CGFloat overscroll = scrollView != nil
|
|
4328
|
+
? -(scrollView.contentOffset.y + scrollView.contentInset.top) : 0;
|
|
4329
|
+
double epochMs = [NSDate.date timeIntervalSince1970] * 1000;
|
|
4330
|
+
double monoMs = now * 1000;
|
|
4331
|
+
RNCCollapsiblePagerLog([NSString stringWithFormat:
|
|
4332
|
+
@"[IOS-HOME-VISUAL] epochMs=%.0f monoMs=%.0f phase=%@ pagerTag=%ld generation=%lu pageIndex=%ld window=%d edge=%d overlay=%.1f header=%ld sticky=%ld collapse=%.1f scrollY=%.1f insetTop=%.1f overscroll=%.1f refresh=%d refreshing=%d controlY=%.1f controlH=%.1f sublayerY=%.1f visualYEstimate=%.1f overlayY=%.1f overlayBottom=%.1f stickyY=%.1f stickyBottom=%.1f maskY=%.1f maskH=%.1f pageMaskWindowY=%.1f",
|
|
4333
|
+
epochMs, monoMs, phase, (long)self.tag, (unsigned long)_generation,
|
|
4334
|
+
(long)_currentIndex, self.window != nil, _topScrollEdgeEnabled,
|
|
4335
|
+
self.effectiveTopOverlayHeight, (long)_headerHeight, (long)_stickyHeaderHeight,
|
|
4336
|
+
_headerOffset, scrollView.contentOffset.y, scrollView.contentInset.top,
|
|
4337
|
+
overscroll, control != nil, control.isRefreshing,
|
|
4338
|
+
CGRectGetMinY(controlWindow), CGRectGetHeight(controlWindow),
|
|
4339
|
+
control != nil ? control.layer.sublayerTransform.m42 : 0, visualYEstimate,
|
|
4340
|
+
CGRectGetMinY(overlayWindow), CGRectGetMaxY(overlayWindow),
|
|
4341
|
+
CGRectGetMinY(stickyWindow), CGRectGetMaxY(stickyWindow),
|
|
4342
|
+
CGRectGetMinY(maskWindow), CGRectGetHeight(maskWindow), pageMaskWindowY]);
|
|
4343
|
+
RNCCollapsiblePagerLog([NSString stringWithFormat:
|
|
4344
|
+
@"[IOS-HOME-VISUAL] epochMs=%.0f monoMs=%.0f phase=%@-refresh-layer pagerTag=%ld generation=%lu pageIndex=%ld boundsY=%.1f boundsH=%.1f centerY=%.1f transformTY=%.1f layerPositionY=%.1f layerTransformY=%.1f directChildCount=%lu firstChildAttached=%d firstChildWindowY=%.1f firstChildWindowH=%.1f",
|
|
4345
|
+
epochMs, monoMs, phase, (long)self.tag, (unsigned long)_generation,
|
|
4346
|
+
(long)_currentIndex, control.bounds.origin.y, control.bounds.size.height,
|
|
4347
|
+
control.center.y, control.transform.ty, control.layer.position.y,
|
|
4348
|
+
control.layer.transform.m42, (unsigned long)control.layer.sublayers.count,
|
|
4349
|
+
childAttached, CGRectGetMinY(firstChildWindow), CGRectGetHeight(firstChildWindow)]);
|
|
4350
|
+
if (![phase isEqualToString:@"pull"]) {
|
|
4351
|
+
CALayer *topCover = _topEdgeSeparatorCover;
|
|
4352
|
+
CALayer *stickyCover = _stickyEdgeSeparatorCover;
|
|
4353
|
+
UIColor *color = topCover.backgroundColor != nil
|
|
4354
|
+
? [UIColor colorWithCGColor:topCover.backgroundColor] : nil;
|
|
4355
|
+
CGFloat red = 0, green = 0, blue = 0, alpha = 0;
|
|
4356
|
+
[color getRed:&red green:&green blue:&blue alpha:&alpha];
|
|
4357
|
+
CGRect topCoverWindow = topCover.superlayer != nil
|
|
4358
|
+
? [topCover convertRect:topCover.bounds toLayer:self.window.layer] : CGRectZero;
|
|
4359
|
+
CGRect stickyCoverWindow = stickyCover.superlayer != nil
|
|
4360
|
+
? [stickyCover convertRect:stickyCover.bounds toLayer:self.window.layer] : CGRectZero;
|
|
4361
|
+
RNCCollapsiblePagerLog([NSString stringWithFormat:
|
|
4362
|
+
@"[IOS-HOME-VISUAL] epochMs=%.0f monoMs=%.0f phase=%@-edge pagerTag=%ld generation=%lu pageIndex=%ld trait=%ld override=%ld style=%ld pinned=%d topHostY=%.1f topHostBottom=%.1f stickyHostY=%.1f stickyHostBottom=%.1f topInteractionSameScroll=%d stickyInteractionSameScroll=%d topCover=%d topCoverHidden=%d topCoverY=%.1f topCoverH=%.3f stickyCover=%d stickyCoverHidden=%d stickyCoverY=%.1f stickyCoverH=%.3f coverRGBA=%.3f,%.3f,%.3f,%.3f scale=%.1f",
|
|
4363
|
+
epochMs, monoMs, phase, (long)self.tag, (unsigned long)_generation,
|
|
4364
|
+
(long)_currentIndex, (long)self.traitCollection.userInterfaceStyle,
|
|
4365
|
+
(long)_topOverlayHostView.overrideUserInterfaceStyle,
|
|
4366
|
+
(long)_topScrollEdgeEffect, _stickyHeaderIsPinned,
|
|
4367
|
+
CGRectGetMinY(overlayWindow), CGRectGetMaxY(overlayWindow),
|
|
4368
|
+
CGRectGetMinY(stickyWindow), CGRectGetMaxY(stickyWindow),
|
|
4369
|
+
_topEdgeInteraction.scrollView == scrollView,
|
|
4370
|
+
_stickyEdgeInteraction.scrollView == scrollView,
|
|
4371
|
+
topCover != nil, topCover.hidden, CGRectGetMinY(topCoverWindow),
|
|
4372
|
+
CGRectGetHeight(topCoverWindow), stickyCover != nil,
|
|
4373
|
+
stickyCover.hidden,
|
|
4374
|
+
CGRectGetMinY(stickyCoverWindow),
|
|
4375
|
+
CGRectGetHeight(stickyCoverWindow), red, green, blue, alpha,
|
|
4376
|
+
self.window.screen.scale]);
|
|
4377
|
+
}
|
|
4378
|
+
#endif
|
|
3899
4379
|
}
|
|
3900
4380
|
|
|
3901
4381
|
- (void)releaseRefreshControlForScrollView:(UIScrollView *)scrollView
|
|
@@ -4189,8 +4669,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4189
4669
|
|
|
4190
4670
|
- (void)updateNativeScrollerViewport:(UIScrollView *)scrollView stickyHeight:(CGFloat)height
|
|
4191
4671
|
{
|
|
4192
|
-
|
|
4193
|
-
|
|
4672
|
+
id host = scrollView.superview;
|
|
4673
|
+
if ([host respondsToSelector:@selector(setPagerStickyHeight:forOwner:)]) {
|
|
4674
|
+
[host setPagerStickyHeight:height forOwner:self];
|
|
4194
4675
|
}
|
|
4195
4676
|
}
|
|
4196
4677
|
|
|
@@ -4262,6 +4743,66 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4262
4743
|
}
|
|
4263
4744
|
|
|
4264
4745
|
|
|
4746
|
+
- (void)nativeScrollViewDidBecomeReadyNotification:(NSNotification *)notification
|
|
4747
|
+
{
|
|
4748
|
+
if ([notification.object isKindOfClass:UIScrollView.class]) {
|
|
4749
|
+
[self nativeScrollViewDidBecomeReady:(UIScrollView *)notification.object];
|
|
4750
|
+
}
|
|
4751
|
+
}
|
|
4752
|
+
|
|
4753
|
+
- (void)nativeScrollViewDidBecomeReady:(UIScrollView *)scrollView
|
|
4754
|
+
{
|
|
4755
|
+
if (_isBeingRecycled || self.window == nil || scrollView == nil ||
|
|
4756
|
+
[_mountingRemovedScrollViews containsObject:scrollView]) return;
|
|
4757
|
+
for (UIView *ancestor = scrollView.superview;
|
|
4758
|
+
ancestor != nil && ancestor != self;
|
|
4759
|
+
ancestor = ancestor.superview) {
|
|
4760
|
+
if ([ancestor isKindOfClass:RNCCollapsiblePagerViewComponentView.class]) {
|
|
4761
|
+
RNCCollapsiblePagerViewComponentView *nestedPager =
|
|
4762
|
+
(RNCCollapsiblePagerViewComponentView *)ancestor;
|
|
4763
|
+
if ([nestedPager currentVerticalScrollViewForParentPager] != scrollView) return;
|
|
4764
|
+
break;
|
|
4765
|
+
}
|
|
4766
|
+
}
|
|
4767
|
+
NSInteger pageIndex = NSNotFound;
|
|
4768
|
+
for (NSInteger index = 0; index < _pageControllers.count; index++) {
|
|
4769
|
+
if ([scrollView isDescendantOfView:_pageControllers[index].view]) {
|
|
4770
|
+
pageIndex = index;
|
|
4771
|
+
break;
|
|
4772
|
+
}
|
|
4773
|
+
}
|
|
4774
|
+
if (pageIndex == NSNotFound) return;
|
|
4775
|
+
|
|
4776
|
+
NSString *pageKey = [self pageKeyForIndex:pageIndex];
|
|
4777
|
+
BOOL hasReleasedStateForPage = _releasedPageScrollStates[pageKey] != nil;
|
|
4778
|
+
BOOL currentPageNeedsOwner = pageIndex == _currentIndex &&
|
|
4779
|
+
(_awaitingScrollOwnerForCurrentPage || _needsScrollObserverReattachAfterMount ||
|
|
4780
|
+
hasReleasedStateForPage ||
|
|
4781
|
+
(_observedScrollView != nil &&
|
|
4782
|
+
[_observedScrollView isKindOfClass:UICollectionView.class] &&
|
|
4783
|
+
!RNCIsMountedNativeListScrollView(_observedScrollView) &&
|
|
4784
|
+
_observedScrollView != scrollView));
|
|
4785
|
+
|
|
4786
|
+
if (!hasReleasedStateForPage && !currentPageNeedsOwner &&
|
|
4787
|
+
!_needsScrollObserverReattachAfterMount) return;
|
|
4788
|
+
|
|
4789
|
+
if (_nativeSmoothHeaderScrollEnabled) {
|
|
4790
|
+
NSInteger first = MAX(0, _currentIndex - 1);
|
|
4791
|
+
NSInteger last = MIN((NSInteger)_pageControllers.count - 1, _currentIndex + 1);
|
|
4792
|
+
BOOL isOwnedScroller = [scrollView.superview isKindOfClass:RNCNativeScrollerComponentView.class] &&
|
|
4793
|
+
((RNCNativeScrollerComponentView *)scrollView.superview).scrollView == scrollView;
|
|
4794
|
+
if ((pageIndex >= first && pageIndex <= last || pageIndex == _destinationIndex) &&
|
|
4795
|
+
(RNCIsMountedNativeListScrollView(scrollView) || isOwnedScroller) &&
|
|
4796
|
+
[_originalInsets objectForKey:scrollView] == nil) {
|
|
4797
|
+
[self applyInsetsToScrollView:scrollView pageIndex:pageIndex restore:YES];
|
|
4798
|
+
}
|
|
4799
|
+
}
|
|
4800
|
+
|
|
4801
|
+
if (currentPageNeedsOwner && !_transitioning && !_isPagerDragging) {
|
|
4802
|
+
[self attachScrollObserverForCurrentPage];
|
|
4803
|
+
}
|
|
4804
|
+
}
|
|
4805
|
+
|
|
4265
4806
|
- (void)nativeScroller:(RNCNativeScrollerComponentView *)scroller
|
|
4266
4807
|
callerInsetChangedFrom:(UIEdgeInsets)previous to:(UIEdgeInsets)next
|
|
4267
4808
|
{
|
|
@@ -4313,6 +4854,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4313
4854
|
if (context == RNCScrollRefreshControlContext) {
|
|
4314
4855
|
UIScrollView *scrollView = (UIScrollView *)object;
|
|
4315
4856
|
[self updateRefreshControlForScrollView:scrollView];
|
|
4857
|
+
[self logHomeVisualGeometry:@"refresh-control-change" scrollView:scrollView];
|
|
4316
4858
|
if (scrollView == _observedScrollView && !_syntheticPageRestore &&
|
|
4317
4859
|
(!_nativeSmoothHeaderScrollEnabled || (!_transitioning && _transitionPageOffsets == nil))) {
|
|
4318
4860
|
[self updateHeaderForScrollView:scrollView];
|
|
@@ -4320,6 +4862,11 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4320
4862
|
return;
|
|
4321
4863
|
}
|
|
4322
4864
|
if (context == RNCCollapsiblePagerContentOffsetContext && object == _observedScrollView) {
|
|
4865
|
+
if ([keyPath isEqualToString:@"contentOffset"] &&
|
|
4866
|
+
-(((UIScrollView *)object).contentOffset.y +
|
|
4867
|
+
((UIScrollView *)object).contentInset.top) > 5) {
|
|
4868
|
+
[self logHomeVisualGeometry:@"pull" scrollView:(UIScrollView *)object];
|
|
4869
|
+
}
|
|
4323
4870
|
if (_pendingTopInsetReboundScrollView == object &&
|
|
4324
4871
|
([keyPath isEqualToString:@"contentOffset"] || [keyPath isEqualToString:@"contentInset"])) {
|
|
4325
4872
|
if (((UIScrollView *)object).panGestureRecognizer.state == UIGestureRecognizerStateChanged) {
|
|
@@ -4978,6 +5525,13 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4978
5525
|
{
|
|
4979
5526
|
UIScrollView *scrollView = _pagerTopScrollView;
|
|
4980
5527
|
if (_dispatchingPagerTop || ![self matchesPagerTop:scrollView]) return;
|
|
5528
|
+
CGFloat clampedTarget = MIN(MAX(_pagerTopLogicalTarget, 0), _headerHeight);
|
|
5529
|
+
if (clampedTarget != _pagerTopLogicalTarget) {
|
|
5530
|
+
_pagerTopLogicalTarget = clampedTarget;
|
|
5531
|
+
CGFloat physicalTarget = RNCLimitRestoredScrollOffset(clampedTarget - scrollView.contentInset.top,
|
|
5532
|
+
scrollView.contentSize.height, scrollView.bounds.size.height, scrollView.contentInset.top, scrollView.contentInset.bottom);
|
|
5533
|
+
[scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x, physicalTarget) animated:NO];
|
|
5534
|
+
}
|
|
4981
5535
|
CGFloat logical = scrollView.contentOffset.y + scrollView.contentInset.top;
|
|
4982
5536
|
if (fabs(logical - _pagerTopLogicalTarget) > 0.5) return;
|
|
4983
5537
|
_pageOffsets[_pagerTopPageKey] = @(_nativeSmoothHeaderScrollEnabled ? 0 : _pagerTopLogicalTarget);
|
|
@@ -4989,11 +5543,29 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4989
5543
|
}
|
|
4990
5544
|
|
|
4991
5545
|
- (void)scrollToTop:(NSInteger)target animated:(BOOL)animated
|
|
5546
|
+
{
|
|
5547
|
+
if (target != 0 && target != 1) return;
|
|
5548
|
+
[self scrollHeaderToOffset:(target == 1 ? _headerHeight : 0) animated:animated];
|
|
5549
|
+
}
|
|
5550
|
+
|
|
5551
|
+
- (void)scrollHeaderBy:(double)delta
|
|
5552
|
+
{
|
|
5553
|
+
if (!std::isfinite(delta)) return;
|
|
5554
|
+
[self scrollHeaderToOffset:MIN(MAX(_headerOffset + delta, 0), _headerHeight) animated:NO];
|
|
5555
|
+
}
|
|
5556
|
+
|
|
5557
|
+
- (void)scrollHeaderToOffset:(double)offset
|
|
5558
|
+
{
|
|
5559
|
+
if (!std::isfinite(offset)) return;
|
|
5560
|
+
[self scrollHeaderToOffset:MIN(MAX(offset, 0), _headerHeight) animated:NO];
|
|
5561
|
+
}
|
|
5562
|
+
|
|
5563
|
+
- (void)scrollHeaderToOffset:(CGFloat)logicalTarget animated:(BOOL)animated
|
|
4992
5564
|
{
|
|
4993
5565
|
[self cancelTopInsetReboundCheck];
|
|
4994
5566
|
UIScrollView *scrollView = _observedScrollView;
|
|
4995
5567
|
NSString *key = _observedPageKey;
|
|
4996
|
-
if (
|
|
5568
|
+
if (_isBeingRecycled || _transitioning || _isPagerDragging ||
|
|
4997
5569
|
_transitionPageOffsets != nil || _syntheticPageRestore || scrollView == nil || scrollView.window == nil ||
|
|
4998
5570
|
key == nil || ![key isEqualToString:self.currentPageKey] || _observedPageIndex != _currentIndex ||
|
|
4999
5571
|
_observedPageGeneration != _generation || self.window == nil) return;
|
|
@@ -5009,7 +5581,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
5009
5581
|
[_alignedBodyKeys removeObject:key];
|
|
5010
5582
|
[_suspendedBodyKeys removeObject:key];
|
|
5011
5583
|
[_displayBodyOverrides removeObjectForKey:key];
|
|
5012
|
-
|
|
5584
|
+
logicalTarget = MIN(MAX(logicalTarget, 0), _headerHeight);
|
|
5013
5585
|
_pageOffsets[key] = @(_nativeSmoothHeaderScrollEnabled ? 0 : logicalTarget);
|
|
5014
5586
|
_pagerTopScrollView = scrollView;
|
|
5015
5587
|
_pagerTopPageKey = [key copy];
|