@onekeyfe/react-native-pager-view 3.0.179-alpha.302 → 3.0.179-alpha.303
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerViewManager.kt +10 -0
- package/android/src/main/java/com/reactnativepagerview/NativeScrollerViewManager.kt +4 -0
- package/docs/SPEC.md +77 -1
- package/ios/RNCCollapsiblePagerViewComponentView.mm +838 -138
- package/ios/RNCNativeScrollerComponentView.mm +93 -38
- package/ios/RNCPagerViewComponentView.h +7 -0
- package/ios/RNCPagerViewComponentView.mm +34 -0
- package/ios/tests/RNCCollapsiblePagerHitTestTests.mm +49 -0
- package/lib/module/CollapsiblePagerView.js +27 -1
- package/lib/module/CollapsiblePagerView.web.js +4 -0
- package/lib/module/CollapsiblePagerViewNativeComponent.ts +6 -0
- package/lib/module/NativeScroller.js +105 -82
- package/lib/module/NativeScroller.web.js +2 -0
- package/lib/module/NativeScrollerNativeComponent.ts +2 -0
- package/lib/typescript/src/CollapsiblePagerView.d.ts +10 -1
- package/lib/typescript/src/CollapsiblePagerView.web.d.ts +4 -0
- package/lib/typescript/src/CollapsiblePagerViewNativeComponent.d.ts +8 -0
- package/lib/typescript/src/NativeScroller.d.ts +4 -0
- package/lib/typescript/src/NativeScrollerNativeComponent.d.ts +2 -0
- package/package.json +2 -2
- package/src/CollapsiblePagerView.tsx +44 -0
- package/src/CollapsiblePagerView.web.tsx +8 -0
- package/src/CollapsiblePagerViewNativeComponent.ts +6 -0
- package/src/NativeScroller.tsx +27 -2
- package/src/NativeScroller.web.tsx +2 -0
- package/src/NativeScrollerNativeComponent.ts +2 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#import "RNCCollapsiblePagerViewComponentView.h"
|
|
2
|
+
#import "RNCPagerViewComponentView.h"
|
|
2
3
|
#import "RNCNativeScrollerComponentView.h"
|
|
3
4
|
#import "RNCCollapsiblePagerReleasedStatePolicy.h"
|
|
4
5
|
|
|
@@ -194,6 +195,23 @@ static BOOL RNCIsEnabledHorizontalScrollOwner(UIScrollView *scroll)
|
|
|
194
195
|
scroll.contentSize.height <= CGRectGetHeight(scroll.bounds) + 0.5;
|
|
195
196
|
}
|
|
196
197
|
|
|
198
|
+
static UIScrollView *RNCNestedOuterReactPagerScrollView(UIView *view)
|
|
199
|
+
{
|
|
200
|
+
Class outerPagerClass = NSClassFromString(@"RNCPagerViewComponentView");
|
|
201
|
+
if (outerPagerClass == Nil) return nil;
|
|
202
|
+
UIScrollView *pagingAncestor = nil;
|
|
203
|
+
for (UIView *ancestor = view.superview; ancestor != nil; ancestor = ancestor.superview) {
|
|
204
|
+
if ([ancestor isKindOfClass:UIScrollView.class] &&
|
|
205
|
+
((UIScrollView *)ancestor).pagingEnabled) {
|
|
206
|
+
pagingAncestor = (UIScrollView *)ancestor;
|
|
207
|
+
}
|
|
208
|
+
if ([ancestor isKindOfClass:outerPagerClass]) {
|
|
209
|
+
return pagingAncestor.panGestureRecognizer.enabled ? pagingAncestor : nil;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return nil;
|
|
213
|
+
}
|
|
214
|
+
|
|
197
215
|
static void RNCGuardNestedHorizontalPans(UIView *view, UIGestureRecognizer *guard)
|
|
198
216
|
{
|
|
199
217
|
if ([view isKindOfClass:UIScrollView.class]) {
|
|
@@ -219,6 +237,7 @@ static void RNCCollapsiblePagerLog(NSString *message)
|
|
|
219
237
|
logFunction(logClass, selector, @"CollapsiblePager", message);
|
|
220
238
|
}
|
|
221
239
|
|
|
240
|
+
|
|
222
241
|
static BOOL RNCGetColorComponents(UIColor *color, UITraitCollection *traits,
|
|
223
242
|
CGFloat *red, CGFloat *green, CGFloat *blue, CGFloat *alpha)
|
|
224
243
|
{
|
|
@@ -1042,7 +1061,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1042
1061
|
CGFloat maximum = MAX(0, inner.contentSize.width - CGRectGetWidth(inner.bounds));
|
|
1043
1062
|
BOOL canScroll = maximum > 0 && (intent.x < 0
|
|
1044
1063
|
? inner.contentOffset.x < maximum - 0.5 : inner.contentOffset.x > 0.5);
|
|
1045
|
-
if (canScroll)
|
|
1064
|
+
if (canScroll) {
|
|
1065
|
+
return NO;
|
|
1066
|
+
}
|
|
1046
1067
|
}
|
|
1047
1068
|
}
|
|
1048
1069
|
if (gestureRecognizer == self.panGestureRecognizer && self.excludedHeaderView != nil) {
|
|
@@ -1052,7 +1073,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1052
1073
|
return NO;
|
|
1053
1074
|
}
|
|
1054
1075
|
}
|
|
1055
|
-
|
|
1076
|
+
BOOL result = [super gestureRecognizerShouldBegin:gestureRecognizer];
|
|
1077
|
+
return result;
|
|
1056
1078
|
}
|
|
1057
1079
|
|
|
1058
1080
|
@end
|
|
@@ -1241,6 +1263,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1241
1263
|
|
|
1242
1264
|
@interface RNCCollapsiblePagerOuterPagerPanGestureRecognizer : UIPanGestureRecognizer
|
|
1243
1265
|
@property (nonatomic, strong) NSHashTable<UIGestureRecognizer *> *blockedPagerGestures;
|
|
1266
|
+
@property (nonatomic, weak) UITouch *trackedTouch;
|
|
1267
|
+
@property (nonatomic, assign) BOOL protectsNestedBody;
|
|
1244
1268
|
@end
|
|
1245
1269
|
|
|
1246
1270
|
@implementation RNCCollapsiblePagerOuterPagerPanGestureRecognizer
|
|
@@ -1265,6 +1289,14 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1265
1289
|
return NO;
|
|
1266
1290
|
}
|
|
1267
1291
|
|
|
1292
|
+
- (void)reset
|
|
1293
|
+
{
|
|
1294
|
+
[super reset];
|
|
1295
|
+
self.trackedTouch = nil;
|
|
1296
|
+
self.protectsNestedBody = NO;
|
|
1297
|
+
self.cancelsTouchesInView = YES;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1268
1300
|
@end
|
|
1269
1301
|
|
|
1270
1302
|
@interface RNCCollapsiblePagerVerticalPagerGuardGestureRecognizer : UIGestureRecognizer
|
|
@@ -1408,7 +1440,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1408
1440
|
UIScrollViewDelegate,
|
|
1409
1441
|
UIGestureRecognizerDelegate,
|
|
1410
1442
|
RCTMountingTransactionObserving,
|
|
1411
|
-
RNCNativeScrollerInsetCoordinator
|
|
1443
|
+
RNCNativeScrollerInsetCoordinator,
|
|
1444
|
+
RNCPagerViewNestedOverlayObserver
|
|
1412
1445
|
>
|
|
1413
1446
|
- (void)finishPagerScrollEmittingSelection:(BOOL)emitSelection;
|
|
1414
1447
|
- (void)completeTransitionOnNextRunLoopForGeneration:(NSUInteger)generation
|
|
@@ -1418,6 +1451,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1418
1451
|
- (void)liftSharedHeadersForPagerTransition:(NSString *)reason;
|
|
1419
1452
|
- (void)restoreSharedHeadersToContainer;
|
|
1420
1453
|
- (void)layoutSharedHeaders;
|
|
1454
|
+
- (void)restorePinnedStickyHeader;
|
|
1455
|
+
- (void)updatePinnedStickyHeader;
|
|
1421
1456
|
- (UIScrollView *)currentVerticalScrollViewForParentPager;
|
|
1422
1457
|
- (UIScrollView *)verticalScrollViewForPageAtIndex:(NSInteger)pageIndex;
|
|
1423
1458
|
- (UIScrollView *)findExplicitNativeScrollerInView:(UIView *)view;
|
|
@@ -1429,19 +1464,32 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1429
1464
|
- (void)detachSharedHeaderPressCancellationGesture;
|
|
1430
1465
|
- (void)updateRefreshControlForScrollView:(UIScrollView *)scrollView;
|
|
1431
1466
|
- (void)releaseRefreshControlForScrollView:(UIScrollView *)scrollView;
|
|
1467
|
+
- (void)cancelTopInsetReboundCheck;
|
|
1468
|
+
- (void)scheduleTopInsetReboundCheck;
|
|
1469
|
+
- (void)syncScrollEdgeAppearance;
|
|
1470
|
+
- (void)updateScrollEdgeContentMasks;
|
|
1471
|
+
- (void)restoreScrollEdgeContentMasks;
|
|
1432
1472
|
- (void)sharedHeaderPressCancellationGestureChanged:(UIPanGestureRecognizer *)recognizer;
|
|
1433
1473
|
- (void)outerPagerGestureChanged:(UIPanGestureRecognizer *)recognizer;
|
|
1434
1474
|
- (void)verticalPagerGuardGestureChanged:(UIPanGestureRecognizer *)recognizer;
|
|
1435
1475
|
- (void)connectVerticalPagerGuard;
|
|
1436
1476
|
- (void)observedListPanChanged:(UIPanGestureRecognizer *)recognizer;
|
|
1477
|
+
- (BOOL)resolveExternalTopOverlay;
|
|
1478
|
+
- (void)registerExternalOuterPager;
|
|
1479
|
+
- (CGFloat)effectiveTopOverlayHeight;
|
|
1480
|
+
- (BOOL)hasFixedTopOverlay;
|
|
1437
1481
|
@end
|
|
1438
1482
|
|
|
1439
1483
|
@implementation RNCCollapsiblePagerViewComponentView {
|
|
1440
|
-
#if DEBUG
|
|
1441
|
-
NSUInteger _offsetDiagSequence;
|
|
1442
|
-
#endif
|
|
1443
1484
|
UIView *_containerView;
|
|
1444
1485
|
RNCCollapsiblePagerSharedHeaderView *_sharedHeaderHostView;
|
|
1486
|
+
UIView *_pinnedStickyHostView;
|
|
1487
|
+
BOOL _stickyHeaderIsPinned;
|
|
1488
|
+
UIView *_sharedHeaderMaskView;
|
|
1489
|
+
UIView *_originalSharedHeaderMaskView;
|
|
1490
|
+
NSMapTable<UIView *, id> *_originalPageMasks;
|
|
1491
|
+
NSMapTable<UIView *, UIView *> *_ownedPageMasks;
|
|
1492
|
+
CGFloat _pageMaskBoundary;
|
|
1445
1493
|
UIPageViewController *_pageViewController;
|
|
1446
1494
|
UIScrollView *_pageViewControllerScrollView;
|
|
1447
1495
|
UIScrollView *_pagerScrollView;
|
|
@@ -1449,6 +1497,16 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1449
1497
|
NSMutableArray<UIViewController *> *_pageControllers;
|
|
1450
1498
|
UIView *_headerView;
|
|
1451
1499
|
UIView *_stickyHeaderView;
|
|
1500
|
+
UIView *_topOverlayView;
|
|
1501
|
+
UIView *_topOverlayHostView;
|
|
1502
|
+
UIScrollEdgeElementContainerInteraction *_topEdgeInteraction;
|
|
1503
|
+
UIScrollEdgeElementContainerInteraction *_stickyEdgeInteraction;
|
|
1504
|
+
UILabel *_topEdgeMarker;
|
|
1505
|
+
UILabel *_stickyEdgeMarker;
|
|
1506
|
+
__weak UIView *_topEdgeInteractionHost;
|
|
1507
|
+
__weak UIView *_stickyEdgeInteractionHost;
|
|
1508
|
+
__weak UIScrollView *_topEdgeScrollView;
|
|
1509
|
+
UIScrollEdgeEffectStyle *_originalTopEdgeStyle;
|
|
1452
1510
|
RNCCollapsiblePagerNativeTabBarView *_nativeTabBarView;
|
|
1453
1511
|
RNCCollapsiblePagerNativeSubHeaderView *_nativeSubHeaderView;
|
|
1454
1512
|
NSInteger _currentIndex;
|
|
@@ -1456,6 +1514,22 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1456
1514
|
NSInteger _pendingInitialPage;
|
|
1457
1515
|
NSInteger _headerHeight;
|
|
1458
1516
|
NSInteger _stickyHeaderHeight;
|
|
1517
|
+
CGFloat _topOverlayHeight;
|
|
1518
|
+
NSInteger _externalTopOverlayTag;
|
|
1519
|
+
__weak UIView *_externalTopOverlayView;
|
|
1520
|
+
CGFloat _externalTopOverlayHeight;
|
|
1521
|
+
__weak RNCPagerViewComponentView *_externalOuterPager;
|
|
1522
|
+
BOOL _externalOuterPageActive;
|
|
1523
|
+
BOOL _externalOuterTransitioning;
|
|
1524
|
+
BOOL _topScrollEdgeHard;
|
|
1525
|
+
NSString *_topOverlayColorScheme;
|
|
1526
|
+
BOOL _pendingTopOverlayAppearanceCommit;
|
|
1527
|
+
BOOL _applyingTopOverlayInsetChange;
|
|
1528
|
+
BOOL _correctingTopInsetRebound;
|
|
1529
|
+
__weak UIScrollView *_pendingTopInsetReboundScrollView;
|
|
1530
|
+
NSString *_pendingTopInsetReboundPageKey;
|
|
1531
|
+
NSUInteger _pendingTopInsetReboundGeneration;
|
|
1532
|
+
NSTimer *_topInsetReboundTimer;
|
|
1459
1533
|
CGFloat _nativeTabBarHeight;
|
|
1460
1534
|
CGFloat _nativeSubHeaderHeight;
|
|
1461
1535
|
CGFloat _headerOffset;
|
|
@@ -1475,6 +1549,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1475
1549
|
NSInteger _pendingGoToIndex;
|
|
1476
1550
|
BOOL _pendingGoToAnimated;
|
|
1477
1551
|
BOOL _needsSlotRebuild;
|
|
1552
|
+
BOOL _deferTopEdgeSlotRebuild;
|
|
1478
1553
|
BOOL _hasAppliedInitialPage;
|
|
1479
1554
|
BOOL _needsPropsReapply;
|
|
1480
1555
|
NSString *_layoutDirection;
|
|
@@ -1512,9 +1587,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1512
1587
|
NSUInteger _observedPageGeneration;
|
|
1513
1588
|
NSDictionary<NSString *, NSNumber *> *_transitionPageOffsets;
|
|
1514
1589
|
NSMutableIndexSet *_transitionCompensatedPages;
|
|
1515
|
-
#if DEBUG
|
|
1516
|
-
NSInteger _transitionDiagBucket;
|
|
1517
|
-
#endif
|
|
1518
1590
|
NSInteger _transitionSourceIndex;
|
|
1519
1591
|
BOOL _syntheticPageRestore;
|
|
1520
1592
|
BOOL _headerSettledAtTop;
|
|
@@ -1580,6 +1652,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1580
1652
|
_originalAlwaysBounceVertical = [NSMapTable weakToStrongObjectsMapTable];
|
|
1581
1653
|
_originalDirectionalLockEnabled = [NSMapTable weakToStrongObjectsMapTable];
|
|
1582
1654
|
_originalInsetAdjustmentBehavior = [NSMapTable weakToStrongObjectsMapTable];
|
|
1655
|
+
_originalPageMasks = [NSMapTable strongToStrongObjectsMapTable];
|
|
1656
|
+
_ownedPageMasks = [NSMapTable strongToStrongObjectsMapTable];
|
|
1583
1657
|
_pageKeys = @[];
|
|
1584
1658
|
_retainedPages = @"[]";
|
|
1585
1659
|
_currentIndex = 0;
|
|
@@ -1607,6 +1681,12 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1607
1681
|
_sharedHeaderHostView = [RNCCollapsiblePagerSharedHeaderView new];
|
|
1608
1682
|
_sharedHeaderHostView.clipsToBounds = NO;
|
|
1609
1683
|
[_containerView addSubview:_sharedHeaderHostView];
|
|
1684
|
+
_pinnedStickyHostView = [UIView new];
|
|
1685
|
+
_pinnedStickyHostView.hidden = YES;
|
|
1686
|
+
[_containerView addSubview:_pinnedStickyHostView];
|
|
1687
|
+
_topOverlayHostView = [UIView new];
|
|
1688
|
+
_topOverlayHostView.hidden = YES;
|
|
1689
|
+
[_containerView addSubview:_topOverlayHostView];
|
|
1610
1690
|
|
|
1611
1691
|
_verticalPagerGesture =
|
|
1612
1692
|
[[RNCCollapsiblePagerVerticalPagerGuardGestureRecognizer alloc]
|
|
@@ -1664,14 +1744,39 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1664
1744
|
viewProps.pointerEvents == PointerEventsMode::BoxOnly) {
|
|
1665
1745
|
return [super hitTest:point withEvent:event];
|
|
1666
1746
|
}
|
|
1747
|
+
if (_topOverlayView != nil) {
|
|
1748
|
+
CGPoint overlayPoint = [_topOverlayView convertPoint:point fromView:self];
|
|
1749
|
+
UIView *overlayHitView = [_topOverlayView hitTest:overlayPoint withEvent:event];
|
|
1750
|
+
if (overlayHitView != nil) return overlayHitView;
|
|
1751
|
+
}
|
|
1752
|
+
if (_stickyHeaderIsPinned) {
|
|
1753
|
+
CGPoint stickyPoint = [_pinnedStickyHostView convertPoint:point fromView:self];
|
|
1754
|
+
UIView *stickyHitView = [_pinnedStickyHostView hitTest:stickyPoint withEvent:event];
|
|
1755
|
+
if (stickyHitView != nil) return stickyHitView;
|
|
1756
|
+
}
|
|
1757
|
+
CGPoint headerPoint = [_sharedHeaderHostView convertPoint:point fromView:self];
|
|
1758
|
+
BOOL sharedHeaderPointIsVisible =
|
|
1759
|
+
_sharedHeaderHostView.maskView != _sharedHeaderMaskView ||
|
|
1760
|
+
_sharedHeaderMaskView == nil ||
|
|
1761
|
+
CGRectContainsPoint(_sharedHeaderMaskView.frame, headerPoint);
|
|
1667
1762
|
if (_nativeSmoothHeaderScrollEnabled && !_isBeingRecycled &&
|
|
1668
|
-
_sharedHeaderHostView.superview != _containerView
|
|
1669
|
-
|
|
1763
|
+
_sharedHeaderHostView.superview != _containerView &&
|
|
1764
|
+
sharedHeaderPointIsVisible) {
|
|
1670
1765
|
UIView *headerHitView =
|
|
1671
1766
|
[_sharedHeaderHostView hitTest:headerPoint withEvent:event];
|
|
1672
1767
|
if (headerHitView != nil) return headerHitView;
|
|
1673
1768
|
}
|
|
1674
|
-
|
|
1769
|
+
UIView *hitView = [super hitTest:point withEvent:event];
|
|
1770
|
+
if (hitView != nil && !sharedHeaderPointIsVisible &&
|
|
1771
|
+
(hitView == _sharedHeaderHostView ||
|
|
1772
|
+
[hitView isDescendantOfView:_sharedHeaderHostView])) return nil;
|
|
1773
|
+
if (hitView != nil && _ownedPageMasks.count > 0 &&
|
|
1774
|
+
[_containerView convertPoint:point fromView:self].y < _pageMaskBoundary) {
|
|
1775
|
+
for (UIView *page in _ownedPageMasks.keyEnumerator.allObjects) {
|
|
1776
|
+
if ([hitView isDescendantOfView:page]) return nil;
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
return hitView;
|
|
1675
1780
|
}
|
|
1676
1781
|
|
|
1677
1782
|
- (void)willMoveToSuperview:(UIView *)newSuperview
|
|
@@ -1686,11 +1791,18 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1686
1791
|
- (void)didMoveToWindow
|
|
1687
1792
|
{
|
|
1688
1793
|
[super didMoveToWindow];
|
|
1794
|
+
[self registerExternalOuterPager];
|
|
1795
|
+
BOOL externalHeightChanged = [self resolveExternalTopOverlay];
|
|
1796
|
+
if (self.window == nil) [self restoreScrollEdgeContentMasks];
|
|
1797
|
+
[self syncScrollEdgeAppearance];
|
|
1798
|
+
[self emitTopScrollEdgeCapability];
|
|
1799
|
+
[self updateTopScrollEdgeForScrollView:_observedScrollView];
|
|
1689
1800
|
[self updateSharedHeaderPressCancellationGesture];
|
|
1690
1801
|
[self updateHeaderSettledStateForceBusy:NO];
|
|
1691
1802
|
for (UIScrollView *scrollView in _originalInsets.keyEnumerator) {
|
|
1692
|
-
[self updateNativeScrollerViewport:scrollView stickyHeight:self.window == nil ? 0 : _stickyHeaderHeight];
|
|
1803
|
+
[self updateNativeScrollerViewport:scrollView stickyHeight:self.window == nil ? 0 : _stickyHeaderHeight + self.effectiveTopOverlayHeight];
|
|
1693
1804
|
}
|
|
1805
|
+
if (externalHeightChanged) [self reapplyInsetsToObservedScrollView];
|
|
1694
1806
|
// OneKey patch: removing a decelerating page from its window can suppress
|
|
1695
1807
|
// UIKit's final scroll callback. Restore the last acknowledged page on reattach.
|
|
1696
1808
|
if (self.window != nil && _isPagerDragging &&
|
|
@@ -1712,8 +1824,320 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1712
1824
|
}
|
|
1713
1825
|
}
|
|
1714
1826
|
|
|
1827
|
+
- (CGFloat)effectiveTopOverlayHeight
|
|
1828
|
+
{
|
|
1829
|
+
return _topOverlayView != nil ? _topOverlayHeight : _externalTopOverlayHeight;
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
- (BOOL)hasFixedTopOverlay
|
|
1833
|
+
{
|
|
1834
|
+
return _topOverlayView != nil ||
|
|
1835
|
+
(_externalTopOverlayTag > 0 && _externalTopOverlayHeight > 0);
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
- (void)registerExternalOuterPager
|
|
1839
|
+
{
|
|
1840
|
+
RNCPagerViewComponentView *outer = nil;
|
|
1841
|
+
if (_externalTopOverlayTag > 0 && self.window != nil && !_isBeingRecycled) {
|
|
1842
|
+
for (UIView *ancestor = self.superview; ancestor != nil; ancestor = ancestor.superview) {
|
|
1843
|
+
if ([ancestor isKindOfClass:RNCPagerViewComponentView.class]) {
|
|
1844
|
+
outer = (RNCPagerViewComponentView *)ancestor;
|
|
1845
|
+
break;
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
if (_externalOuterPager == outer) return;
|
|
1850
|
+
[_externalOuterPager removeNestedOverlayObserver:self];
|
|
1851
|
+
_externalOuterPager = outer;
|
|
1852
|
+
_externalOuterPageActive = NO;
|
|
1853
|
+
_externalOuterTransitioning = NO;
|
|
1854
|
+
[outer addNestedOverlayObserver:self];
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
- (BOOL)resolveExternalTopOverlay
|
|
1858
|
+
{
|
|
1859
|
+
UIView *nextView = nil;
|
|
1860
|
+
CGFloat nextHeight = 0;
|
|
1861
|
+
if (_externalTopOverlayTag > 0 && _topOverlayView == nil &&
|
|
1862
|
+
self.window != nil && !_isBeingRecycled) {
|
|
1863
|
+
for (UIView *ancestor = self.superview; ancestor != nil; ancestor = ancestor.superview) {
|
|
1864
|
+
UIView *candidate = [ancestor viewWithTag:_externalTopOverlayTag];
|
|
1865
|
+
if (candidate == nil || candidate == self ||
|
|
1866
|
+
[candidate isDescendantOfView:self] || candidate.window != self.window) continue;
|
|
1867
|
+
nextView = candidate;
|
|
1868
|
+
break;
|
|
1869
|
+
}
|
|
1870
|
+
if (nextView != nil && CGRectGetHeight(nextView.bounds) > 0) {
|
|
1871
|
+
nextHeight = MAX(0, CGRectGetMaxY([nextView convertRect:nextView.bounds
|
|
1872
|
+
toView:_containerView]));
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
if (nextView == nil && _externalOuterTransitioning &&
|
|
1876
|
+
_externalTopOverlayTag > 0 && self.window != nil) {
|
|
1877
|
+
nextHeight = _externalTopOverlayHeight;
|
|
1878
|
+
}
|
|
1879
|
+
BOOL changed = _externalTopOverlayView != nextView ||
|
|
1880
|
+
fabs(_externalTopOverlayHeight - nextHeight) > 0.5;
|
|
1881
|
+
_externalTopOverlayView = nextView;
|
|
1882
|
+
_externalTopOverlayHeight = nextHeight;
|
|
1883
|
+
_topOverlayHostView.hidden = !self.hasFixedTopOverlay;
|
|
1884
|
+
_topOverlayHostView.userInteractionEnabled = _topOverlayView != nil;
|
|
1885
|
+
return changed;
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
- (void)outerPagerDidUpdateOverlayOwner:(BOOL)active transitioning:(BOOL)transitioning
|
|
1889
|
+
{
|
|
1890
|
+
if (_isBeingRecycled || _externalOuterPager == nil || _externalTopOverlayTag <= 0) return;
|
|
1891
|
+
if (_externalOuterPageActive == active && _externalOuterTransitioning == transitioning) return;
|
|
1892
|
+
BOOL wasTransitioning = _externalOuterTransitioning;
|
|
1893
|
+
_externalOuterPageActive = active;
|
|
1894
|
+
_externalOuterTransitioning = transitioning;
|
|
1895
|
+
if (transitioning && !wasTransitioning) {
|
|
1896
|
+
[self updateTopScrollEdgeForScrollView:nil];
|
|
1897
|
+
[self liftSharedHeadersForPagerTransition:@"outer"];
|
|
1898
|
+
} else if (!transitioning && wasTransitioning &&
|
|
1899
|
+
!_transitioning && !_isPagerDragging && active) {
|
|
1900
|
+
[self attachSharedHeadersToScrollView:_observedScrollView];
|
|
1901
|
+
}
|
|
1902
|
+
[self updateTopScrollEdgeForScrollView:transitioning ? nil : _observedScrollView];
|
|
1903
|
+
[self updateScrollEdgeContentMasks];
|
|
1904
|
+
[self setNeedsLayout];
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
|
|
1908
|
+
{
|
|
1909
|
+
[super traitCollectionDidChange:previousTraitCollection];
|
|
1910
|
+
if (previousTraitCollection.userInterfaceStyle != self.traitCollection.userInterfaceStyle) {
|
|
1911
|
+
[self syncScrollEdgeAppearance];
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
- (void)syncScrollEdgeAppearance
|
|
1916
|
+
{
|
|
1917
|
+
BOOL active = _topScrollEdgeHard &&
|
|
1918
|
+
(self.hasFixedTopOverlay || _stickyHeaderView != nil) && self.window != nil &&
|
|
1919
|
+
!_isBeingRecycled;
|
|
1920
|
+
UIUserInterfaceStyle style = UIUserInterfaceStyleUnspecified;
|
|
1921
|
+
if (active) {
|
|
1922
|
+
if ([_topOverlayColorScheme isEqualToString:@"dark"]) {
|
|
1923
|
+
style = UIUserInterfaceStyleDark;
|
|
1924
|
+
} else if ([_topOverlayColorScheme isEqualToString:@"light"]) {
|
|
1925
|
+
style = UIUserInterfaceStyleLight;
|
|
1926
|
+
} else {
|
|
1927
|
+
style = self.traitCollection.userInterfaceStyle;
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
if (_topOverlayHostView.overrideUserInterfaceStyle == style) return;
|
|
1931
|
+
_topOverlayHostView.overrideUserInterfaceStyle = style;
|
|
1932
|
+
_pinnedStickyHostView.overrideUserInterfaceStyle = style;
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
- (void)restoreScrollEdgeContentMasks
|
|
1936
|
+
{
|
|
1937
|
+
if (_sharedHeaderHostView.maskView == _sharedHeaderMaskView) {
|
|
1938
|
+
_sharedHeaderHostView.maskView = _originalSharedHeaderMaskView;
|
|
1939
|
+
}
|
|
1940
|
+
_sharedHeaderMaskView = nil;
|
|
1941
|
+
_originalSharedHeaderMaskView = nil;
|
|
1942
|
+
for (UIView *page in _ownedPageMasks.keyEnumerator.allObjects) {
|
|
1943
|
+
if (page.maskView == [_ownedPageMasks objectForKey:page]) {
|
|
1944
|
+
id original = [_originalPageMasks objectForKey:page];
|
|
1945
|
+
page.maskView = original == NSNull.null ? nil : original;
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
[_ownedPageMasks removeAllObjects];
|
|
1949
|
+
[_originalPageMasks removeAllObjects];
|
|
1950
|
+
_pageMaskBoundary = 0;
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1953
|
+
- (void)updateScrollEdgeContentMasks
|
|
1954
|
+
{
|
|
1955
|
+
BOOL enabled = _topScrollEdgeHard &&
|
|
1956
|
+
(self.hasFixedTopOverlay || _stickyHeaderView != nil) &&
|
|
1957
|
+
self.window != nil && !_isBeingRecycled;
|
|
1958
|
+
if (!enabled) {
|
|
1959
|
+
[self restoreScrollEdgeContentMasks];
|
|
1960
|
+
return;
|
|
1961
|
+
}
|
|
1962
|
+
if (self.hasFixedTopOverlay && _sharedHeaderHostView.window == self.window &&
|
|
1963
|
+
CGRectGetHeight(_sharedHeaderHostView.bounds) > 0 &&
|
|
1964
|
+
CGRectGetHeight(_topOverlayHostView.bounds) > 0) {
|
|
1965
|
+
if (_sharedHeaderMaskView == nil) {
|
|
1966
|
+
_originalSharedHeaderMaskView = _sharedHeaderHostView.maskView;
|
|
1967
|
+
_sharedHeaderMaskView = [UIView new];
|
|
1968
|
+
_sharedHeaderMaskView.backgroundColor = UIColor.blackColor;
|
|
1969
|
+
_sharedHeaderHostView.maskView = _sharedHeaderMaskView;
|
|
1970
|
+
}
|
|
1971
|
+
CGFloat topBottom = CGRectGetMaxY([_topOverlayHostView
|
|
1972
|
+
convertRect:_topOverlayHostView.bounds toView:_containerView]);
|
|
1973
|
+
CGFloat localTop = [_containerView convertPoint:CGPointMake(0, topBottom)
|
|
1974
|
+
toView:_sharedHeaderHostView].y;
|
|
1975
|
+
_sharedHeaderMaskView.frame = CGRectMake(0, localTop,
|
|
1976
|
+
CGRectGetWidth(_sharedHeaderHostView.bounds),
|
|
1977
|
+
MAX(0, CGRectGetHeight(_sharedHeaderHostView.bounds) - localTop));
|
|
1978
|
+
} else if (_sharedHeaderMaskView != nil) {
|
|
1979
|
+
if (_sharedHeaderHostView.maskView == _sharedHeaderMaskView) {
|
|
1980
|
+
_sharedHeaderHostView.maskView = _originalSharedHeaderMaskView;
|
|
1981
|
+
}
|
|
1982
|
+
_sharedHeaderMaskView = nil;
|
|
1983
|
+
_originalSharedHeaderMaskView = nil;
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
BOOL transitioning = _transitioning || _isPagerDragging || _externalOuterTransitioning ||
|
|
1987
|
+
_sharedHeadersLiftedForPagerTransition;
|
|
1988
|
+
CGFloat boundary = CGRectGetMaxY([_topOverlayHostView
|
|
1989
|
+
convertRect:_topOverlayHostView.bounds toView:_containerView]);
|
|
1990
|
+
UIView *sticky = _stickyHeaderIsPinned ? _pinnedStickyHostView : _stickyHeaderView;
|
|
1991
|
+
if (sticky != nil && !sticky.hidden && sticky.window == self.window &&
|
|
1992
|
+
CGRectGetHeight(sticky.bounds) > 0) {
|
|
1993
|
+
boundary = MAX(boundary, CGRectGetMaxY([sticky
|
|
1994
|
+
convertRect:sticky.bounds toView:_containerView]));
|
|
1995
|
+
}
|
|
1996
|
+
_pageMaskBoundary = boundary;
|
|
1997
|
+
NSMutableSet<UIView *> *visiblePages = [NSMutableSet new];
|
|
1998
|
+
if (transitioning) {
|
|
1999
|
+
for (UIViewController *controller in _pageControllers) {
|
|
2000
|
+
UIView *page = controller.view;
|
|
2001
|
+
if (page.window != self.window ||
|
|
2002
|
+
!CGRectIntersectsRect([page convertRect:page.bounds toView:_containerView],
|
|
2003
|
+
_containerView.bounds)) continue;
|
|
2004
|
+
[visiblePages addObject:page];
|
|
2005
|
+
UIView *mask = [_ownedPageMasks objectForKey:page];
|
|
2006
|
+
if (mask == nil) {
|
|
2007
|
+
[_originalPageMasks setObject:page.maskView ?: NSNull.null forKey:page];
|
|
2008
|
+
mask = [UIView new];
|
|
2009
|
+
mask.backgroundColor = UIColor.blackColor;
|
|
2010
|
+
[_ownedPageMasks setObject:mask forKey:page];
|
|
2011
|
+
page.maskView = mask;
|
|
2012
|
+
}
|
|
2013
|
+
CGFloat localTop = [_containerView convertPoint:CGPointMake(0, boundary)
|
|
2014
|
+
toView:page].y;
|
|
2015
|
+
mask.frame = CGRectMake(0, localTop, CGRectGetWidth(page.bounds),
|
|
2016
|
+
MAX(0, CGRectGetHeight(page.bounds) - localTop));
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
for (UIView *page in _ownedPageMasks.keyEnumerator.allObjects) {
|
|
2020
|
+
if ([visiblePages containsObject:page]) continue;
|
|
2021
|
+
UIView *mask = [_ownedPageMasks objectForKey:page];
|
|
2022
|
+
if (page.maskView == mask) {
|
|
2023
|
+
id original = [_originalPageMasks objectForKey:page];
|
|
2024
|
+
page.maskView = original == NSNull.null ? nil : original;
|
|
2025
|
+
}
|
|
2026
|
+
[_ownedPageMasks removeObjectForKey:page];
|
|
2027
|
+
[_originalPageMasks removeObjectForKey:page];
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
- (void)updateEventEmitter:(const EventEmitter::Shared &)eventEmitter
|
|
2032
|
+
{
|
|
2033
|
+
[super updateEventEmitter:eventEmitter];
|
|
2034
|
+
[self emitTopScrollEdgeCapability];
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
- (void)emitTopScrollEdgeCapability
|
|
2038
|
+
{
|
|
2039
|
+
if (self.window == nil || _isBeingRecycled) return;
|
|
2040
|
+
const auto emitter = [self eventEmitter];
|
|
2041
|
+
if (!emitter) return;
|
|
2042
|
+
BOOL available = NO;
|
|
2043
|
+
if (@available(iOS 26.0, *)) available = YES;
|
|
2044
|
+
emitter->onScrollEdgeCapability({.available = (bool)available});
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
- (void)updateTopScrollEdgeForScrollView:(UIScrollView *)scrollView
|
|
2048
|
+
{
|
|
2049
|
+
[self syncScrollEdgeAppearance];
|
|
2050
|
+
if (@available(iOS 26.0, *)) {
|
|
2051
|
+
if (_pendingTopOverlayAppearanceCommit) scrollView = nil;
|
|
2052
|
+
BOOL externalOwnerReady = _externalTopOverlayTag <= 0 ||
|
|
2053
|
+
_externalOuterPager == nil ||
|
|
2054
|
+
(_externalOuterPageActive && !_externalOuterTransitioning);
|
|
2055
|
+
BOOL hasOverlay = _topScrollEdgeHard && self.hasFixedTopOverlay &&
|
|
2056
|
+
externalOwnerReady && self.window != nil;
|
|
2057
|
+
BOOL hasSticky = _topScrollEdgeHard && _stickyHeaderView != nil &&
|
|
2058
|
+
externalOwnerReady && self.window != nil;
|
|
2059
|
+
BOOL enabled = (hasOverlay || hasSticky) && scrollView != nil;
|
|
2060
|
+
BOOL topHostChanged = (_topEdgeInteraction != nil) != hasOverlay ||
|
|
2061
|
+
(hasOverlay && _topEdgeInteractionHost != _topOverlayHostView);
|
|
2062
|
+
if (_topEdgeScrollView != nil && (!enabled || _topEdgeScrollView != scrollView ||
|
|
2063
|
+
topHostChanged)) {
|
|
2064
|
+
[_topEdgeInteractionHost removeInteraction:_topEdgeInteraction];
|
|
2065
|
+
_topEdgeInteraction.scrollView = nil;
|
|
2066
|
+
[_stickyEdgeInteractionHost removeInteraction:_stickyEdgeInteraction];
|
|
2067
|
+
_stickyEdgeInteraction.scrollView = nil;
|
|
2068
|
+
[_topEdgeMarker removeFromSuperview];
|
|
2069
|
+
[_stickyEdgeMarker removeFromSuperview];
|
|
2070
|
+
_topEdgeScrollView.topEdgeEffect.style = _originalTopEdgeStyle;
|
|
2071
|
+
_topEdgeInteraction = nil;
|
|
2072
|
+
_stickyEdgeInteraction = nil;
|
|
2073
|
+
_topEdgeMarker = nil;
|
|
2074
|
+
_stickyEdgeMarker = nil;
|
|
2075
|
+
_topEdgeInteractionHost = nil;
|
|
2076
|
+
_stickyEdgeInteractionHost = nil;
|
|
2077
|
+
_topEdgeScrollView = nil;
|
|
2078
|
+
_originalTopEdgeStyle = nil;
|
|
2079
|
+
}
|
|
2080
|
+
if (enabled && _topEdgeScrollView == nil) {
|
|
2081
|
+
_originalTopEdgeStyle = scrollView.topEdgeEffect.style;
|
|
2082
|
+
_topEdgeScrollView = scrollView;
|
|
2083
|
+
scrollView.topEdgeEffect.style = UIScrollEdgeEffectStyle.hardStyle;
|
|
2084
|
+
}
|
|
2085
|
+
if (hasOverlay && enabled && _topEdgeInteraction == nil) {
|
|
2086
|
+
// UIKit needs a native element to shape the edge effect around React-drawn content.
|
|
2087
|
+
_topEdgeMarker = [UILabel new];
|
|
2088
|
+
_topEdgeMarker.frame = _topOverlayHostView.bounds;
|
|
2089
|
+
_topEdgeMarker.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
2090
|
+
_topEdgeMarker.text = @" ";
|
|
2091
|
+
_topEdgeMarker.textColor = UIColor.clearColor;
|
|
2092
|
+
_topEdgeMarker.backgroundColor = UIColor.clearColor;
|
|
2093
|
+
_topEdgeMarker.userInteractionEnabled = NO;
|
|
2094
|
+
_topEdgeMarker.isAccessibilityElement = NO;
|
|
2095
|
+
_topEdgeMarker.accessibilityElementsHidden = YES;
|
|
2096
|
+
[_topOverlayHostView addSubview:_topEdgeMarker];
|
|
2097
|
+
_topEdgeInteraction = [UIScrollEdgeElementContainerInteraction new];
|
|
2098
|
+
_topEdgeInteraction.edge = UIRectEdgeTop;
|
|
2099
|
+
_topEdgeInteraction.scrollView = scrollView;
|
|
2100
|
+
[_topOverlayHostView addInteraction:_topEdgeInteraction];
|
|
2101
|
+
_topEdgeInteractionHost = _topOverlayHostView;
|
|
2102
|
+
}
|
|
2103
|
+
BOOL legacyStickyPinned = !_nativeSmoothHeaderScrollEnabled &&
|
|
2104
|
+
_stickyHeaderView != nil && _headerOffset >= _headerHeight - 0.5;
|
|
2105
|
+
BOOL stickyEnabled = enabled && (_stickyHeaderIsPinned || legacyStickyPinned);
|
|
2106
|
+
UIView *stickyInteractionHost = _stickyHeaderIsPinned
|
|
2107
|
+
? _pinnedStickyHostView : _stickyHeaderView;
|
|
2108
|
+
if (_stickyEdgeInteractionHost != nil &&
|
|
2109
|
+
(!stickyEnabled || _stickyEdgeInteractionHost != stickyInteractionHost)) {
|
|
2110
|
+
[_stickyEdgeInteractionHost removeInteraction:_stickyEdgeInteraction];
|
|
2111
|
+
_stickyEdgeInteraction.scrollView = nil;
|
|
2112
|
+
[_stickyEdgeMarker removeFromSuperview];
|
|
2113
|
+
_stickyEdgeMarker = nil;
|
|
2114
|
+
_stickyEdgeInteraction = nil;
|
|
2115
|
+
_stickyEdgeInteractionHost = nil;
|
|
2116
|
+
}
|
|
2117
|
+
if (stickyEnabled && _stickyEdgeInteraction == nil) {
|
|
2118
|
+
_stickyEdgeMarker = [UILabel new];
|
|
2119
|
+
_stickyEdgeMarker.frame = stickyInteractionHost.bounds;
|
|
2120
|
+
_stickyEdgeMarker.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
2121
|
+
_stickyEdgeMarker.text = @" ";
|
|
2122
|
+
_stickyEdgeMarker.textColor = UIColor.clearColor;
|
|
2123
|
+
_stickyEdgeMarker.backgroundColor = UIColor.clearColor;
|
|
2124
|
+
_stickyEdgeMarker.userInteractionEnabled = NO;
|
|
2125
|
+
_stickyEdgeMarker.isAccessibilityElement = NO;
|
|
2126
|
+
_stickyEdgeMarker.accessibilityElementsHidden = YES;
|
|
2127
|
+
[stickyInteractionHost addSubview:_stickyEdgeMarker];
|
|
2128
|
+
_stickyEdgeInteraction = [UIScrollEdgeElementContainerInteraction new];
|
|
2129
|
+
_stickyEdgeInteraction.edge = UIRectEdgeTop;
|
|
2130
|
+
_stickyEdgeInteraction.scrollView = scrollView;
|
|
2131
|
+
[stickyInteractionHost addInteraction:_stickyEdgeInteraction];
|
|
2132
|
+
_stickyEdgeInteractionHost = stickyInteractionHost;
|
|
2133
|
+
}
|
|
2134
|
+
[self updateScrollEdgeContentMasks];
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
|
|
1715
2138
|
- (void)dealloc
|
|
1716
2139
|
{
|
|
2140
|
+
[self updateTopScrollEdgeForScrollView:nil];
|
|
1717
2141
|
[self cancelHeaderSettledCheck];
|
|
1718
2142
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"OneKeyNativeListScrollCommandAccepted" object:nil];
|
|
1719
2143
|
for (UIScrollView *scrollView in _originalInsets.keyEnumerator) {
|
|
@@ -1766,6 +2190,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1766
2190
|
_hasPendingDirectNativePagerChange = NO;
|
|
1767
2191
|
if (_directNativePagerEnabled == enabled) return;
|
|
1768
2192
|
|
|
2193
|
+
[self restoreScrollEdgeContentMasks];
|
|
1769
2194
|
[self detachScrollObserver];
|
|
1770
2195
|
[self restoreSharedHeadersToContainer];
|
|
1771
2196
|
if (_blockerGesture != nil) {
|
|
@@ -1841,8 +2266,33 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1841
2266
|
|
|
1842
2267
|
#pragma mark - Smooth shared header ownership
|
|
1843
2268
|
|
|
2269
|
+
- (void)restorePinnedStickyHeader
|
|
2270
|
+
{
|
|
2271
|
+
if (!_stickyHeaderIsPinned) return;
|
|
2272
|
+
_stickyHeaderIsPinned = NO;
|
|
2273
|
+
_stickyHeaderView.transform = CGAffineTransformIdentity;
|
|
2274
|
+
_nativeTabBarView.transform = CGAffineTransformIdentity;
|
|
2275
|
+
_nativeSubHeaderView.transform = CGAffineTransformIdentity;
|
|
2276
|
+
[_stickyHeaderView removeFromSuperview];
|
|
2277
|
+
[_nativeTabBarView removeFromSuperview];
|
|
2278
|
+
[_nativeSubHeaderView removeFromSuperview];
|
|
2279
|
+
if (_stickyHeaderView != nil) [_sharedHeaderHostView addSubview:_stickyHeaderView];
|
|
2280
|
+
[_sharedHeaderHostView addSubview:_nativeTabBarView];
|
|
2281
|
+
[_sharedHeaderHostView addSubview:_nativeSubHeaderView];
|
|
2282
|
+
_pinnedStickyHostView.hidden = YES;
|
|
2283
|
+
_pinnedStickyHostView.layer.zPosition = 0;
|
|
2284
|
+
[_containerView addSubview:_pinnedStickyHostView];
|
|
2285
|
+
if ([_pagerScrollView isKindOfClass:RNCCollapsiblePagerDirectScrollView.class]) {
|
|
2286
|
+
((RNCCollapsiblePagerDirectScrollView *)_pagerScrollView).excludedHeaderView = _sharedHeaderHostView;
|
|
2287
|
+
}
|
|
2288
|
+
[_nativeTabBarView updateAncestorGesturePrecedence];
|
|
2289
|
+
[_nativeSubHeaderView updateAncestorGesturePrecedence];
|
|
2290
|
+
}
|
|
2291
|
+
|
|
1844
2292
|
- (void)restoreSharedHeadersToContainer
|
|
1845
2293
|
{
|
|
2294
|
+
[self restorePinnedStickyHeader];
|
|
2295
|
+
[self updateTopScrollEdgeForScrollView:_observedScrollView];
|
|
1846
2296
|
_sharedHeaderScrollView = nil;
|
|
1847
2297
|
_sharedHeadersLiftedForPagerTransition = NO;
|
|
1848
2298
|
_sharedHeaderHostView.layer.zPosition = 0;
|
|
@@ -1850,11 +2300,15 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1850
2300
|
[_containerView addSubview:_sharedHeaderHostView];
|
|
1851
2301
|
}
|
|
1852
2302
|
[self layoutSharedHeaders];
|
|
2303
|
+
if (self.hasFixedTopOverlay) {
|
|
2304
|
+
[_containerView bringSubviewToFront:_topOverlayHostView];
|
|
2305
|
+
}
|
|
1853
2306
|
}
|
|
1854
2307
|
|
|
1855
2308
|
- (void)attachSharedHeadersToScrollView:(UIScrollView *)scrollView
|
|
1856
2309
|
{
|
|
1857
2310
|
if (!_nativeSmoothHeaderScrollEnabled || _transitioning || _isPagerDragging ||
|
|
2311
|
+
_externalOuterTransitioning ||
|
|
1858
2312
|
scrollView == nil || _isBeingRecycled) return;
|
|
1859
2313
|
if ((UIView *)scrollView == _sharedHeaderHostView ||
|
|
1860
2314
|
[scrollView isDescendantOfView:_sharedHeaderHostView]) {
|
|
@@ -1866,13 +2320,16 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1866
2320
|
}
|
|
1867
2321
|
if (_sharedHeaderScrollView == scrollView &&
|
|
1868
2322
|
_sharedHeaderHostView.superview == scrollView) {
|
|
2323
|
+
_sharedHeaderHostView.layer.zPosition =
|
|
2324
|
+
_topScrollEdgeHard && (self.hasFixedTopOverlay || _stickyHeaderView != nil) ? 0 : 1000;
|
|
1869
2325
|
[self layoutSharedHeaders];
|
|
1870
2326
|
return;
|
|
1871
2327
|
}
|
|
1872
2328
|
|
|
1873
2329
|
_sharedHeaderScrollView = scrollView;
|
|
1874
2330
|
_sharedHeadersLiftedForPagerTransition = NO;
|
|
1875
|
-
_sharedHeaderHostView.layer.zPosition =
|
|
2331
|
+
_sharedHeaderHostView.layer.zPosition =
|
|
2332
|
+
_topScrollEdgeHard && (self.hasFixedTopOverlay || _stickyHeaderView != nil) ? 0 : 1000;
|
|
1876
2333
|
if (_sharedHeaderHostView.superview != scrollView) {
|
|
1877
2334
|
[scrollView addSubview:_sharedHeaderHostView];
|
|
1878
2335
|
}
|
|
@@ -1919,56 +2376,114 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1919
2376
|
|
|
1920
2377
|
_sharedHeaderHostView.frame = CGRectMake(
|
|
1921
2378
|
0,
|
|
1922
|
-
attachedToList ? -totalHeight :
|
|
2379
|
+
attachedToList ? -totalHeight : self.effectiveTopOverlayHeight,
|
|
1923
2380
|
width,
|
|
1924
2381
|
totalHeight
|
|
1925
2382
|
);
|
|
1926
2383
|
CGFloat stickyY = _headerHeight;
|
|
1927
2384
|
_headerView.frame = CGRectMake(0, 0, width, _headerHeight);
|
|
1928
|
-
|
|
2385
|
+
CGPoint pinnedOrigin = _stickyHeaderIsPinned
|
|
2386
|
+
? [_sharedHeaderScrollView convertPoint:CGPointMake(0, self.effectiveTopOverlayHeight) fromView:_containerView]
|
|
2387
|
+
: CGPointMake(0, self.effectiveTopOverlayHeight);
|
|
2388
|
+
_pinnedStickyHostView.frame = CGRectMake(
|
|
2389
|
+
pinnedOrigin.x, pinnedOrigin.y, width, _stickyHeaderHeight);
|
|
2390
|
+
CGFloat stickyChildY = _stickyHeaderIsPinned ? 0 : stickyY;
|
|
2391
|
+
_stickyHeaderView.frame = CGRectMake(0, stickyChildY, width, _stickyHeaderHeight);
|
|
1929
2392
|
CGFloat nativeTabBarVisibleHeight = _nativeTabBarView.hidden
|
|
1930
2393
|
? 0
|
|
1931
2394
|
: MIN(_stickyHeaderHeight, _nativeTabBarHeight);
|
|
1932
2395
|
_nativeTabBarView.frame = CGRectMake(
|
|
1933
2396
|
0,
|
|
1934
|
-
|
|
2397
|
+
stickyChildY,
|
|
1935
2398
|
width,
|
|
1936
2399
|
nativeTabBarVisibleHeight
|
|
1937
2400
|
);
|
|
1938
2401
|
_nativeSubHeaderView.frame = CGRectMake(
|
|
1939
2402
|
0,
|
|
1940
|
-
|
|
2403
|
+
stickyChildY + nativeTabBarVisibleHeight,
|
|
1941
2404
|
width,
|
|
1942
2405
|
MIN(
|
|
1943
2406
|
MAX(0, _stickyHeaderHeight - nativeTabBarVisibleHeight),
|
|
1944
2407
|
_nativeSubHeaderHeight
|
|
1945
2408
|
)
|
|
1946
2409
|
);
|
|
2410
|
+
_topOverlayHostView.frame = CGRectMake(0, 0, width, self.effectiveTopOverlayHeight);
|
|
2411
|
+
_topOverlayView.frame = _topOverlayHostView.bounds;
|
|
1947
2412
|
[self applyHeaderOffset];
|
|
1948
2413
|
}
|
|
1949
2414
|
|
|
1950
|
-
- (void)
|
|
2415
|
+
- (void)updatePinnedStickyHeader
|
|
1951
2416
|
{
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
if (!
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
(
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
2417
|
+
BOOL attachedToList = _nativeSmoothHeaderScrollEnabled &&
|
|
2418
|
+
_sharedHeaderScrollView != nil && !_sharedHeadersLiftedForPagerTransition;
|
|
2419
|
+
if (!attachedToList && !_stickyHeaderIsPinned) return;
|
|
2420
|
+
UIScrollView *scrollView = _sharedHeaderScrollView;
|
|
2421
|
+
NSNumber *appliedTop = [_appliedTopInsets objectForKey:scrollView];
|
|
2422
|
+
CGFloat refreshInset = scrollView.refreshControl != nil && appliedTop != nil
|
|
2423
|
+
? MAX(0, scrollView.contentInset.top - appliedTop.doubleValue) : 0;
|
|
2424
|
+
CGFloat pinnedTranslation = MAX(0, _currentLogicalOffset - _headerHeight - refreshInset);
|
|
2425
|
+
CGFloat stickyTop = [_sharedHeaderHostView convertPoint:
|
|
2426
|
+
CGPointMake(0, _headerHeight + pinnedTranslation) toView:self].y;
|
|
2427
|
+
BOOL shouldPin = _topScrollEdgeHard &&
|
|
2428
|
+
_stickyHeaderView != nil && attachedToList && !_transitioning &&
|
|
2429
|
+
!_externalOuterTransitioning &&
|
|
2430
|
+
!_isPagerDragging && _headerOffset >= _headerHeight - 0.5 &&
|
|
2431
|
+
stickyTop <= self.effectiveTopOverlayHeight + 0.5;
|
|
2432
|
+
if (_stickyHeaderIsPinned && shouldPin && _pinnedStickyHostView.superview != scrollView) {
|
|
2433
|
+
[self updateTopScrollEdgeForScrollView:nil];
|
|
2434
|
+
[scrollView addSubview:_pinnedStickyHostView];
|
|
2435
|
+
[self updateTopScrollEdgeForScrollView:_observedScrollView];
|
|
2436
|
+
}
|
|
2437
|
+
if (_stickyHeaderIsPinned == shouldPin) {
|
|
2438
|
+
if (shouldPin) {
|
|
2439
|
+
CGPoint origin = [scrollView convertPoint:CGPointMake(0, self.effectiveTopOverlayHeight)
|
|
2440
|
+
fromView:_containerView];
|
|
2441
|
+
_pinnedStickyHostView.frame = CGRectMake(origin.x, origin.y,
|
|
2442
|
+
CGRectGetWidth(self.bounds), _stickyHeaderHeight);
|
|
2443
|
+
}
|
|
2444
|
+
return;
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
_stickyHeaderIsPinned = shouldPin;
|
|
2448
|
+
UIView *host = shouldPin ? _pinnedStickyHostView : _sharedHeaderHostView;
|
|
2449
|
+
_stickyHeaderView.transform = CGAffineTransformIdentity;
|
|
2450
|
+
_nativeTabBarView.transform = CGAffineTransformIdentity;
|
|
2451
|
+
_nativeSubHeaderView.transform = CGAffineTransformIdentity;
|
|
2452
|
+
[_stickyHeaderView removeFromSuperview];
|
|
2453
|
+
[_nativeTabBarView removeFromSuperview];
|
|
2454
|
+
[_nativeSubHeaderView removeFromSuperview];
|
|
2455
|
+
if (_stickyHeaderView != nil) [host addSubview:_stickyHeaderView];
|
|
2456
|
+
[host addSubview:_nativeTabBarView];
|
|
2457
|
+
[host addSubview:_nativeSubHeaderView];
|
|
2458
|
+
CGFloat childY = shouldPin ? 0 : _headerHeight;
|
|
2459
|
+
CGFloat width = CGRectGetWidth(self.bounds);
|
|
2460
|
+
CGFloat tabHeight = _nativeTabBarView.hidden ? 0 : MIN(_stickyHeaderHeight, _nativeTabBarHeight);
|
|
2461
|
+
_stickyHeaderView.frame = CGRectMake(0, childY, width, _stickyHeaderHeight);
|
|
2462
|
+
_nativeTabBarView.frame = CGRectMake(0, childY, width, tabHeight);
|
|
2463
|
+
_nativeSubHeaderView.frame = CGRectMake(0, childY + tabHeight, width,
|
|
2464
|
+
MIN(MAX(0, _stickyHeaderHeight - tabHeight), _nativeSubHeaderHeight));
|
|
2465
|
+
_pinnedStickyHostView.hidden = !shouldPin;
|
|
2466
|
+
if ([_pagerScrollView isKindOfClass:RNCCollapsiblePagerDirectScrollView.class]) {
|
|
2467
|
+
((RNCCollapsiblePagerDirectScrollView *)_pagerScrollView).excludedHeaderView =
|
|
2468
|
+
shouldPin ? _pinnedStickyHostView : _sharedHeaderHostView;
|
|
2469
|
+
}
|
|
2470
|
+
if (shouldPin) {
|
|
2471
|
+
[scrollView addSubview:_pinnedStickyHostView];
|
|
2472
|
+
_pinnedStickyHostView.layer.zPosition = 1000;
|
|
2473
|
+
CGPoint origin = [scrollView convertPoint:CGPointMake(0, self.effectiveTopOverlayHeight)
|
|
2474
|
+
fromView:_containerView];
|
|
2475
|
+
_pinnedStickyHostView.frame = CGRectMake(origin.x, origin.y, width, _stickyHeaderHeight);
|
|
2476
|
+
} else {
|
|
2477
|
+
_pinnedStickyHostView.layer.zPosition = 0;
|
|
2478
|
+
[_containerView addSubview:_pinnedStickyHostView];
|
|
2479
|
+
}
|
|
2480
|
+
[_nativeTabBarView updateAncestorGesturePrecedence];
|
|
2481
|
+
[_nativeSubHeaderView updateAncestorGesturePrecedence];
|
|
2482
|
+
[self updateTopScrollEdgeForScrollView:_observedScrollView];
|
|
1967
2483
|
}
|
|
1968
2484
|
|
|
1969
2485
|
- (void)preparePageForHorizontalTransitionAtIndex:(NSInteger)pageIndex
|
|
1970
2486
|
{
|
|
1971
|
-
[self offsetDiag:@"prepare-check" page:pageIndex scroll:nil];
|
|
1972
2487
|
if (!_nativeSmoothHeaderScrollEnabled ||
|
|
1973
2488
|
pageIndex < 0 || pageIndex >= _pageControllers.count) return;
|
|
1974
2489
|
UIScrollView *scrollView = [self verticalScrollViewForPageAtIndex:pageIndex];
|
|
@@ -1987,11 +2502,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1987
2502
|
_transitionHeaderOffset = _headerOffset;
|
|
1988
2503
|
_transitionPageOffsets = [_pageOffsets copy];
|
|
1989
2504
|
_transitionCompensatedPages = [NSMutableIndexSet new];
|
|
1990
|
-
#if DEBUG
|
|
1991
|
-
_transitionDiagBucket = -1;
|
|
1992
|
-
[self offsetDiag:[NSString stringWithFormat:@"screen-begin-source=%ld-savedQ=%.4f", (long)_transitionSourceIndex,
|
|
1993
|
-
[_transitionPageOffsets[self.currentPageKey] doubleValue]] page:_currentIndex scroll:nil];
|
|
1994
|
-
#endif
|
|
1995
2505
|
}
|
|
1996
2506
|
|
|
1997
2507
|
- (void)updatePageStateTransitionProgress:(CGFloat)progress
|
|
@@ -2008,16 +2518,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2008
2518
|
CGFloat firstQ = [_transitionPageOffsets[[self pageKeyForIndex:first]] doubleValue];
|
|
2009
2519
|
CGFloat secondQ = [_transitionPageOffsets[[self pageKeyForIndex:second]] doubleValue];
|
|
2010
2520
|
_headerOffset = _transitionHeaderOffset;
|
|
2011
|
-
#if DEBUG
|
|
2012
|
-
NSInteger bucket = (NSInteger)floor(fraction * 4);
|
|
2013
|
-
if (_transitionDiagBucket != bucket) {
|
|
2014
|
-
_transitionDiagBucket = bucket;
|
|
2015
|
-
[self offsetDiag:[NSString stringWithFormat:
|
|
2016
|
-
@"screen-progress-kind=%@-source=%ld-target=%ld-sourceQ=%.4f-targetQ=%.4f-position=%.4f-fraction=%.4f",
|
|
2017
|
-
_isPagerDragging ? @"interactive" : @"programmatic-endpoint", (long)first, (long)second,
|
|
2018
|
-
firstQ, secondQ, progress, fraction] page:_currentIndex scroll:nil];
|
|
2019
|
-
}
|
|
2020
|
-
#endif
|
|
2021
2521
|
_syntheticPageRestore = YES;
|
|
2022
2522
|
NSInteger visibleFirst = MAX(0, MIN((NSInteger)floor(progress), (NSInteger)_pageControllers.count - 1));
|
|
2023
2523
|
NSInteger visibleLast = MIN(visibleFirst + 1, (NSInteger)_pageControllers.count - 1);
|
|
@@ -2059,10 +2559,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2059
2559
|
_transitionPageOffsets = nil;
|
|
2060
2560
|
_transitionCompensatedPages = nil;
|
|
2061
2561
|
[self layoutSharedHeaders];
|
|
2062
|
-
#if DEBUG
|
|
2063
|
-
[self offsetDiag:[NSString stringWithFormat:@"screen-finish-source=%ld-cancelled=%d", (long)_transitionSourceIndex,
|
|
2064
|
-
_currentIndex == _transitionSourceIndex] page:_currentIndex scroll:nil];
|
|
2065
|
-
#endif
|
|
2066
2562
|
}
|
|
2067
2563
|
|
|
2068
2564
|
#pragma mark - React mounting
|
|
@@ -2119,9 +2615,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2119
2615
|
if (!_nativeSmoothHeaderScrollEnabled && _transitionPageOffsets == nil && !_syntheticPageRestore &&
|
|
2120
2616
|
![_pendingPageRestoreKeys containsObject:pageKey] &&
|
|
2121
2617
|
(ownsObservedPage || _pageOffsets[pageKey] == nil)) {
|
|
2122
|
-
[self offsetDiag:@"mount-release-save-before" page:index scroll:scrollView];
|
|
2123
2618
|
_pageOffsets[pageKey] = @(scrollView.contentOffset.y + scrollView.contentInset.top);
|
|
2124
|
-
[self offsetDiag:@"mount-release-save-after" page:index scroll:scrollView];
|
|
2125
2619
|
}
|
|
2126
2620
|
break;
|
|
2127
2621
|
}
|
|
@@ -2137,6 +2631,27 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2137
2631
|
- (void)mountingTransactionDidMount:(const MountingTransaction &)transaction
|
|
2138
2632
|
withSurfaceTelemetry:(const SurfaceTelemetry &)surfaceTelemetry
|
|
2139
2633
|
{
|
|
2634
|
+
if (_needsSlotRebuild && !_deferTopEdgeSlotRebuild &&
|
|
2635
|
+
!_transitioning && !_isPagerDragging) {
|
|
2636
|
+
[self rebuildSlots];
|
|
2637
|
+
}
|
|
2638
|
+
[self registerExternalOuterPager];
|
|
2639
|
+
if ([self resolveExternalTopOverlay]) {
|
|
2640
|
+
[self reapplyInsetsToObservedScrollView];
|
|
2641
|
+
[self setNeedsLayout];
|
|
2642
|
+
}
|
|
2643
|
+
if (_pendingTopOverlayAppearanceCommit) {
|
|
2644
|
+
_pendingTopOverlayAppearanceCommit = NO;
|
|
2645
|
+
BOOL localOverlayReady = _topOverlayView.superview == _topOverlayHostView &&
|
|
2646
|
+
_topOverlayView.window == self.window;
|
|
2647
|
+
BOOL externalOverlayReady = _externalTopOverlayTag > 0 &&
|
|
2648
|
+
_externalTopOverlayView.window == self.window;
|
|
2649
|
+
if (!_isBeingRecycled && _topScrollEdgeHard && self.window != nil &&
|
|
2650
|
+
(localOverlayReady || externalOverlayReady)) {
|
|
2651
|
+
[self updateTopScrollEdgeForScrollView:_observedScrollView];
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
[self updateScrollEdgeContentMasks];
|
|
2140
2655
|
[_mountingRemovedScrollViews removeAllObjects];
|
|
2141
2656
|
if (!_nativeSmoothHeaderScrollEnabled || _isBeingRecycled || self.window == nil ||
|
|
2142
2657
|
_currentIndex < 0 || _currentIndex >= _pageControllers.count) return;
|
|
@@ -2214,7 +2729,11 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2214
2729
|
if (_isBeingRecycled) return;
|
|
2215
2730
|
NSInteger safeIndex = MIN(MAX(index, 0), _logicalChildren.count);
|
|
2216
2731
|
[_logicalChildren insertObject:childComponentView atIndex:safeIndex];
|
|
2217
|
-
|
|
2732
|
+
if (_topScrollEdgeHard || _topOverlayView != nil) {
|
|
2733
|
+
_needsSlotRebuild = YES;
|
|
2734
|
+
} else {
|
|
2735
|
+
[self rebuildSlots];
|
|
2736
|
+
}
|
|
2218
2737
|
}
|
|
2219
2738
|
|
|
2220
2739
|
- (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView
|
|
@@ -2225,16 +2744,32 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2225
2744
|
if (existingIndex == NSNotFound) return;
|
|
2226
2745
|
[childComponentView removeFromSuperview];
|
|
2227
2746
|
[_logicalChildren removeObjectAtIndex:existingIndex];
|
|
2228
|
-
|
|
2747
|
+
if (_topScrollEdgeHard || _topOverlayView != nil) {
|
|
2748
|
+
_needsSlotRebuild = YES;
|
|
2749
|
+
} else {
|
|
2750
|
+
[self rebuildSlots];
|
|
2751
|
+
}
|
|
2229
2752
|
}
|
|
2230
2753
|
|
|
2231
2754
|
- (void)rebuildSlots
|
|
2232
2755
|
{
|
|
2756
|
+
if (_deferTopEdgeSlotRebuild) {
|
|
2757
|
+
_needsSlotRebuild = YES;
|
|
2758
|
+
return;
|
|
2759
|
+
}
|
|
2233
2760
|
if (_transitioning || _isPagerDragging) {
|
|
2234
2761
|
_needsSlotRebuild = YES;
|
|
2235
2762
|
return;
|
|
2236
2763
|
}
|
|
2764
|
+
if (_topScrollEdgeHard &&
|
|
2765
|
+
_logicalChildren.count != _pageKeys.count + 2 &&
|
|
2766
|
+
_logicalChildren.count != _pageKeys.count + 3) {
|
|
2767
|
+
_needsSlotRebuild = YES;
|
|
2768
|
+
return;
|
|
2769
|
+
}
|
|
2237
2770
|
_needsSlotRebuild = NO;
|
|
2771
|
+
_deferTopEdgeSlotRebuild = NO;
|
|
2772
|
+
[self restoreScrollEdgeContentMasks];
|
|
2238
2773
|
[self detachScrollObserver];
|
|
2239
2774
|
[self restoreSharedHeadersToContainer];
|
|
2240
2775
|
// OneKey patch: Fabric reuses these views without resetting transforms
|
|
@@ -2246,11 +2781,15 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2246
2781
|
_nativeSubHeaderView.transform = CGAffineTransformIdentity;
|
|
2247
2782
|
[_headerView removeFromSuperview];
|
|
2248
2783
|
[_stickyHeaderView removeFromSuperview];
|
|
2784
|
+
[self updateTopScrollEdgeForScrollView:nil];
|
|
2785
|
+
[_topOverlayView removeFromSuperview];
|
|
2249
2786
|
for (UIViewController *controller in _pageControllers) {
|
|
2250
2787
|
[controller.view removeFromSuperview];
|
|
2251
2788
|
}
|
|
2252
2789
|
_headerView = nil;
|
|
2253
2790
|
_stickyHeaderView = nil;
|
|
2791
|
+
_topOverlayView = nil;
|
|
2792
|
+
[self updateTopScrollEdgeForScrollView:nil];
|
|
2254
2793
|
[_pageControllers removeAllObjects];
|
|
2255
2794
|
|
|
2256
2795
|
if (_logicalChildren.count > 0) {
|
|
@@ -2261,7 +2800,15 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2261
2800
|
_stickyHeaderView = _logicalChildren[1];
|
|
2262
2801
|
[_sharedHeaderHostView addSubview:_stickyHeaderView];
|
|
2263
2802
|
}
|
|
2264
|
-
|
|
2803
|
+
BOOL hasOverlaySlot = _topScrollEdgeHard &&
|
|
2804
|
+
_logicalChildren.count == _pageKeys.count + 3;
|
|
2805
|
+
NSInteger pageSlotStart = hasOverlaySlot ? 3 : 2;
|
|
2806
|
+
if (hasOverlaySlot) {
|
|
2807
|
+
_topOverlayView = _logicalChildren[2];
|
|
2808
|
+
[_topOverlayHostView addSubview:_topOverlayView];
|
|
2809
|
+
}
|
|
2810
|
+
[self resolveExternalTopOverlay];
|
|
2811
|
+
for (NSInteger index = pageSlotStart; index < _logicalChildren.count; index++) {
|
|
2265
2812
|
UIView *page = _logicalChildren[index];
|
|
2266
2813
|
UIViewController *controller = [UIViewController new];
|
|
2267
2814
|
[controller.view addSubview:page];
|
|
@@ -2300,6 +2847,10 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2300
2847
|
if (!_nativeTabBarView.hidden) [_sharedHeaderHostView bringSubviewToFront:_nativeTabBarView];
|
|
2301
2848
|
if (!_nativeSubHeaderView.hidden) [_sharedHeaderHostView bringSubviewToFront:_nativeSubHeaderView];
|
|
2302
2849
|
[_containerView bringSubviewToFront:_sharedHeaderHostView];
|
|
2850
|
+
if (_stickyHeaderIsPinned) {
|
|
2851
|
+
[_pinnedStickyHostView.superview bringSubviewToFront:_pinnedStickyHostView];
|
|
2852
|
+
}
|
|
2853
|
+
if (self.hasFixedTopOverlay) [_containerView bringSubviewToFront:_topOverlayHostView];
|
|
2303
2854
|
[self setNeedsLayout];
|
|
2304
2855
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
2305
2856
|
if (!self->_isBeingRecycled) {
|
|
@@ -2312,7 +2863,10 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2312
2863
|
- (void)layoutSubviews
|
|
2313
2864
|
{
|
|
2314
2865
|
[super layoutSubviews];
|
|
2866
|
+
[self syncScrollEdgeAppearance];
|
|
2315
2867
|
_containerView.frame = self.bounds;
|
|
2868
|
+
[self registerExternalOuterPager];
|
|
2869
|
+
BOOL externalHeightChanged = [self resolveExternalTopOverlay];
|
|
2316
2870
|
if (_directNativePagerEnabled) {
|
|
2317
2871
|
_pagerScrollView.frame = _containerView.bounds;
|
|
2318
2872
|
} else {
|
|
@@ -2375,6 +2929,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2375
2929
|
}
|
|
2376
2930
|
}
|
|
2377
2931
|
[self layoutSharedHeaders];
|
|
2932
|
+
if (externalHeightChanged) [self reapplyInsetsToObservedScrollView];
|
|
2933
|
+
[self updateTopScrollEdgeForScrollView:_observedScrollView];
|
|
2934
|
+
[self updateScrollEdgeContentMasks];
|
|
2378
2935
|
[self updateHeaderSettledStateForceBusy:NO];
|
|
2379
2936
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
2380
2937
|
if (!self->_isBeingRecycled) {
|
|
@@ -2386,9 +2943,16 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2386
2943
|
|
|
2387
2944
|
- (void)prepareForRecycle
|
|
2388
2945
|
{
|
|
2946
|
+
[_externalOuterPager removeNestedOverlayObserver:self];
|
|
2947
|
+
_externalOuterPager = nil;
|
|
2948
|
+
[self cancelTopInsetReboundCheck];
|
|
2949
|
+
[self restoreScrollEdgeContentMasks];
|
|
2950
|
+
[self updateTopScrollEdgeForScrollView:nil];
|
|
2389
2951
|
[self cancelHeaderSettledCheck];
|
|
2390
2952
|
[self publishHeaderSettledAtTop:NO identityChanged:NO];
|
|
2391
2953
|
_isBeingRecycled = YES;
|
|
2954
|
+
_pendingTopOverlayAppearanceCommit = NO;
|
|
2955
|
+
[self syncScrollEdgeAppearance];
|
|
2392
2956
|
_generation++;
|
|
2393
2957
|
[_mountingRemovedScrollViews removeAllObjects];
|
|
2394
2958
|
_needsScrollObserverReattachAfterMount = NO;
|
|
@@ -2408,9 +2972,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2408
2972
|
if (appliedTop != nil && scrollView.refreshControl != nil) {
|
|
2409
2973
|
original.top += MAX(0, scrollView.contentInset.top - appliedTop.doubleValue);
|
|
2410
2974
|
}
|
|
2411
|
-
[self offsetDiag:@"release-original-inset-before" page:_currentIndex scroll:scrollView];
|
|
2412
2975
|
scrollView.contentInset = original;
|
|
2413
|
-
[self offsetDiag:@"release-original-inset-after" page:_currentIndex scroll:scrollView];
|
|
2414
2976
|
}
|
|
2415
2977
|
NSValue *indicatorValue = [_originalIndicatorInsets objectForKey:scrollView];
|
|
2416
2978
|
if (indicatorValue != nil) {
|
|
@@ -2439,6 +3001,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2439
3001
|
_nativeSubHeaderView.transform = CGAffineTransformIdentity;
|
|
2440
3002
|
[_headerView removeFromSuperview];
|
|
2441
3003
|
[_stickyHeaderView removeFromSuperview];
|
|
3004
|
+
[_topOverlayView removeFromSuperview];
|
|
2442
3005
|
for (UIViewController *controller in _pageControllers) {
|
|
2443
3006
|
[controller.view removeFromSuperview];
|
|
2444
3007
|
}
|
|
@@ -2446,6 +3009,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2446
3009
|
[_pageControllers removeAllObjects];
|
|
2447
3010
|
_headerView = nil;
|
|
2448
3011
|
_stickyHeaderView = nil;
|
|
3012
|
+
_topOverlayView = nil;
|
|
3013
|
+
_topOverlayHostView.hidden = YES;
|
|
3014
|
+
[self updateTopScrollEdgeForScrollView:nil];
|
|
2449
3015
|
_pageViewController.dataSource = nil;
|
|
2450
3016
|
_pageViewController.delegate = nil;
|
|
2451
3017
|
_pagerScrollView.delegate = nil;
|
|
@@ -2478,6 +3044,13 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2478
3044
|
_pendingInitialPage = 0;
|
|
2479
3045
|
_headerHeight = 0;
|
|
2480
3046
|
_stickyHeaderHeight = 0;
|
|
3047
|
+
_topOverlayHeight = 0;
|
|
3048
|
+
_externalTopOverlayTag = 0;
|
|
3049
|
+
_externalTopOverlayView = nil;
|
|
3050
|
+
_externalTopOverlayHeight = 0;
|
|
3051
|
+
_externalOuterPageActive = NO;
|
|
3052
|
+
_externalOuterTransitioning = NO;
|
|
3053
|
+
_topScrollEdgeHard = NO;
|
|
2481
3054
|
_nativeTabBarHeight = 44;
|
|
2482
3055
|
_nativeSubHeaderHeight = 74;
|
|
2483
3056
|
_headerOffset = 0;
|
|
@@ -2501,6 +3074,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2501
3074
|
_pendingGoToIndex = -1;
|
|
2502
3075
|
_pendingGoToAnimated = YES;
|
|
2503
3076
|
_needsSlotRebuild = NO;
|
|
3077
|
+
_deferTopEdgeSlotRebuild = NO;
|
|
2504
3078
|
_hasAppliedInitialPage = NO;
|
|
2505
3079
|
// OneKey patch: Fabric retains old props when recycling this view.
|
|
2506
3080
|
_needsPropsReapply = YES;
|
|
@@ -2517,6 +3091,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2517
3091
|
{
|
|
2518
3092
|
const auto &oldViewProps = *std::static_pointer_cast<const RNCCollapsiblePagerViewProps>(_props);
|
|
2519
3093
|
const auto &newViewProps = *std::static_pointer_cast<const RNCCollapsiblePagerViewProps>(props);
|
|
3094
|
+
_applyingTopOverlayInsetChange =
|
|
3095
|
+
oldViewProps.topScrollEdgeHard != newViewProps.topScrollEdgeHard ||
|
|
3096
|
+
oldViewProps.topOverlayHeight != newViewProps.topOverlayHeight;
|
|
2520
3097
|
|
|
2521
3098
|
if (_needsPropsReapply || oldViewProps.initialPage != newViewProps.initialPage) {
|
|
2522
3099
|
_pendingInitialPage = MAX(0, newViewProps.initialPage);
|
|
@@ -2566,6 +3143,43 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2566
3143
|
_stickyHeaderHeight = MAX(0, newViewProps.stickyHeaderHeight);
|
|
2567
3144
|
insetsChanged = YES;
|
|
2568
3145
|
}
|
|
3146
|
+
if (_needsPropsReapply || oldViewProps.topScrollEdgeHard != newViewProps.topScrollEdgeHard) {
|
|
3147
|
+
_topScrollEdgeHard = newViewProps.topScrollEdgeHard;
|
|
3148
|
+
if (oldViewProps.topScrollEdgeHard || newViewProps.topScrollEdgeHard) {
|
|
3149
|
+
_deferTopEdgeSlotRebuild = YES;
|
|
3150
|
+
[self rebuildSlots];
|
|
3151
|
+
NSUInteger generation = _generation;
|
|
3152
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
3153
|
+
if (self->_generation != generation || self->_isBeingRecycled ||
|
|
3154
|
+
!self->_deferTopEdgeSlotRebuild) return;
|
|
3155
|
+
self->_deferTopEdgeSlotRebuild = NO;
|
|
3156
|
+
[self rebuildSlots];
|
|
3157
|
+
});
|
|
3158
|
+
}
|
|
3159
|
+
insetsChanged = YES;
|
|
3160
|
+
}
|
|
3161
|
+
if (_needsPropsReapply ||
|
|
3162
|
+
oldViewProps.topOverlayColorScheme != newViewProps.topOverlayColorScheme) {
|
|
3163
|
+
NSString *nextScheme = RCTNSStringFromString(newViewProps.topOverlayColorScheme);
|
|
3164
|
+
if (self.hasFixedTopOverlay && _topScrollEdgeHard &&
|
|
3165
|
+
_topOverlayColorScheme != nil &&
|
|
3166
|
+
![_topOverlayColorScheme isEqualToString:nextScheme]) {
|
|
3167
|
+
_pendingTopOverlayAppearanceCommit = YES;
|
|
3168
|
+
[self updateTopScrollEdgeForScrollView:nil];
|
|
3169
|
+
}
|
|
3170
|
+
_topOverlayColorScheme = nextScheme;
|
|
3171
|
+
[self syncScrollEdgeAppearance];
|
|
3172
|
+
}
|
|
3173
|
+
if (_needsPropsReapply || oldViewProps.topOverlayHeight != newViewProps.topOverlayHeight) {
|
|
3174
|
+
_topOverlayHeight = MAX(0, newViewProps.topOverlayHeight);
|
|
3175
|
+
insetsChanged = YES;
|
|
3176
|
+
}
|
|
3177
|
+
if (_needsPropsReapply ||
|
|
3178
|
+
oldViewProps.externalTopOverlayTag != newViewProps.externalTopOverlayTag) {
|
|
3179
|
+
_externalTopOverlayTag = newViewProps.externalTopOverlayTag;
|
|
3180
|
+
[self registerExternalOuterPager];
|
|
3181
|
+
insetsChanged = [self resolveExternalTopOverlay] || insetsChanged;
|
|
3182
|
+
}
|
|
2569
3183
|
if (_needsPropsReapply || oldViewProps.pageKeys != newViewProps.pageKeys) {
|
|
2570
3184
|
NSString *json = RCTNSStringFromString(newViewProps.pageKeys);
|
|
2571
3185
|
NSData *data = [json dataUsingEncoding:NSUTF8StringEncoding];
|
|
@@ -2633,17 +3247,23 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2633
3247
|
|
|
2634
3248
|
_needsPropsReapply = NO;
|
|
2635
3249
|
[super updateProps:props oldProps:oldProps];
|
|
3250
|
+
[self updateTopScrollEdgeForScrollView:_observedScrollView];
|
|
2636
3251
|
if (insetsChanged) {
|
|
2637
3252
|
[self reapplyInsetsToObservedScrollView];
|
|
2638
3253
|
[self setNeedsLayout];
|
|
2639
3254
|
}
|
|
3255
|
+
_applyingTopOverlayInsetChange = NO;
|
|
2640
3256
|
if (!_nativeTabBarView.hidden) {
|
|
2641
|
-
[
|
|
3257
|
+
[_nativeTabBarView.superview bringSubviewToFront:_nativeTabBarView];
|
|
2642
3258
|
}
|
|
2643
3259
|
if (!_nativeSubHeaderView.hidden) {
|
|
2644
|
-
[
|
|
3260
|
+
[_nativeSubHeaderView.superview bringSubviewToFront:_nativeSubHeaderView];
|
|
2645
3261
|
}
|
|
2646
3262
|
[_sharedHeaderHostView.superview bringSubviewToFront:_sharedHeaderHostView];
|
|
3263
|
+
if (_stickyHeaderIsPinned) {
|
|
3264
|
+
[_pinnedStickyHostView.superview bringSubviewToFront:_pinnedStickyHostView];
|
|
3265
|
+
}
|
|
3266
|
+
if (self.hasFixedTopOverlay) [_containerView bringSubviewToFront:_topOverlayHostView];
|
|
2647
3267
|
}
|
|
2648
3268
|
|
|
2649
3269
|
#pragma mark - Collapsible scroll coordination
|
|
@@ -2877,21 +3497,17 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2877
3497
|
return;
|
|
2878
3498
|
}
|
|
2879
3499
|
|
|
2880
|
-
[self offsetDiag:@"observer-candidate" page:_currentIndex scroll:candidate];
|
|
2881
3500
|
BOOL replacingCurrentScrollView = _observingContentOffset &&
|
|
2882
3501
|
(!_nativeSmoothHeaderScrollEnabled || [_observedPageKey isEqualToString:self.currentPageKey]);
|
|
2883
3502
|
[self detachScrollObserver];
|
|
2884
3503
|
if (replacingCurrentScrollView) {
|
|
2885
3504
|
// Legacy replacement discards body state; smooth replacement waits for layout.
|
|
2886
|
-
[self offsetDiag:@"observer-replacement-cache-before" page:_currentIndex scroll:candidate];
|
|
2887
3505
|
if (!_nativeSmoothHeaderScrollEnabled) _pageOffsets[self.currentPageKey] = @(_headerOffset);
|
|
2888
|
-
[self offsetDiag:@"observer-replacement-cache-after" page:_currentIndex scroll:candidate];
|
|
2889
3506
|
}
|
|
2890
3507
|
_observedScrollView = candidate;
|
|
2891
3508
|
_observedPageKey = [self.currentPageKey copy];
|
|
2892
3509
|
_observedPageIndex = _currentIndex;
|
|
2893
3510
|
_observedPageGeneration = _generation;
|
|
2894
|
-
[self offsetDiag:@"observer-attached-before-restore" page:_currentIndex scroll:candidate];
|
|
2895
3511
|
[self applyInsetsToScrollView:candidate pageIndex:_currentIndex restore:YES];
|
|
2896
3512
|
[candidate addObserver:self
|
|
2897
3513
|
forKeyPath:@"contentOffset"
|
|
@@ -2909,6 +3525,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2909
3525
|
_observingContentOffset = YES;
|
|
2910
3526
|
_needsScrollObserverReattachAfterMount = NO;
|
|
2911
3527
|
_currentLogicalOffset = candidate.contentOffset.y + candidate.contentInset.top;
|
|
3528
|
+
[self updateTopScrollEdgeForScrollView:candidate];
|
|
2912
3529
|
[self attachSharedHeadersToScrollView:candidate];
|
|
2913
3530
|
[self updateSharedHeaderPressCancellationGesture];
|
|
2914
3531
|
[self updateHeaderSettledStateForceBusy:NO];
|
|
@@ -2916,6 +3533,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2916
3533
|
|
|
2917
3534
|
- (void)detachScrollObserver
|
|
2918
3535
|
{
|
|
3536
|
+
[self cancelTopInsetReboundCheck];
|
|
3537
|
+
[self restorePinnedStickyHeader];
|
|
3538
|
+
[self updateTopScrollEdgeForScrollView:nil];
|
|
2919
3539
|
[self cancelHeaderSettledCheck];
|
|
2920
3540
|
_headerCommandOwner = nil;
|
|
2921
3541
|
_headerSettledGeometry = nil;
|
|
@@ -2925,7 +3545,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2925
3545
|
UIScrollView *scrollView = _observedScrollView;
|
|
2926
3546
|
if (scrollView != nil) {
|
|
2927
3547
|
[scrollView.panGestureRecognizer removeTarget:self action:@selector(observedListPanChanged:)];
|
|
2928
|
-
[self offsetDiag:@"detach-save-before" page:_currentIndex scroll:scrollView];
|
|
2929
3548
|
NSString *ownerKey = _nativeSmoothHeaderScrollEnabled ? _observedPageKey : self.currentPageKey;
|
|
2930
3549
|
BOOL ownerKeyExists = ownerKey != nil && ([_pageKeys containsObject:ownerKey] ||
|
|
2931
3550
|
(_observedPageIndex >= 0 && _observedPageIndex < _pageControllers.count &&
|
|
@@ -2933,7 +3552,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2933
3552
|
if (ownerKey != nil && !_nativeSmoothHeaderScrollEnabled) {
|
|
2934
3553
|
_pageOffsets[ownerKey] = @(scrollView.contentOffset.y + scrollView.contentInset.top);
|
|
2935
3554
|
}
|
|
2936
|
-
[self offsetDiag:@"detach-save-after" page:_currentIndex scroll:scrollView];
|
|
2937
3555
|
if (_observingContentOffset) {
|
|
2938
3556
|
[scrollView removeObserver:self
|
|
2939
3557
|
forKeyPath:@"contentOffset"
|
|
@@ -2975,6 +3593,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2975
3593
|
removeGestureRecognizer:_sharedHeaderOuterPagerGesture];
|
|
2976
3594
|
}
|
|
2977
3595
|
_sharedHeaderOuterPagerGesture.blockedPagerGestures = nil;
|
|
3596
|
+
_sharedHeaderOuterPagerGesture.trackedTouch = nil;
|
|
3597
|
+
_sharedHeaderOuterPagerGesture.protectsNestedBody = NO;
|
|
3598
|
+
_sharedHeaderOuterPagerGesture.cancelsTouchesInView = YES;
|
|
2978
3599
|
_sharedHeaderPressCancellationGestureHost = nil;
|
|
2979
3600
|
_reactTouchHandler = nil;
|
|
2980
3601
|
}
|
|
@@ -3024,7 +3645,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3024
3645
|
}
|
|
3025
3646
|
}
|
|
3026
3647
|
if (_sharedHeaderPressCancellationGesture.view == gestureHost &&
|
|
3027
|
-
_sharedHeaderOuterPagerGesture.view ==
|
|
3648
|
+
_sharedHeaderOuterPagerGesture.view == _containerView &&
|
|
3028
3649
|
_reactTouchHandler == reactTouchHandler) {
|
|
3029
3650
|
_sharedHeaderOuterPagerGesture.blockedPagerGestures = outerPagerGestures;
|
|
3030
3651
|
return;
|
|
@@ -3066,7 +3687,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3066
3687
|
_reactTouchHandler = reactTouchHandler;
|
|
3067
3688
|
_sharedHeaderPressCancellationGestureHost = gestureHost;
|
|
3068
3689
|
[gestureHost addGestureRecognizer:_sharedHeaderPressCancellationGesture];
|
|
3069
|
-
[
|
|
3690
|
+
[_containerView addGestureRecognizer:_sharedHeaderOuterPagerGesture];
|
|
3070
3691
|
RNCCollapsiblePagerLog([NSString stringWithFormat:
|
|
3071
3692
|
@"press-cancel-bridge attached=1 outer-pagers=%lu",
|
|
3072
3693
|
(unsigned long)outerPagerGestures.count]);
|
|
@@ -3138,6 +3759,11 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3138
3759
|
|
|
3139
3760
|
- (void)observedListPanChanged:(UIPanGestureRecognizer *)recognizer
|
|
3140
3761
|
{
|
|
3762
|
+
if (recognizer == _pendingTopInsetReboundScrollView.panGestureRecognizer &&
|
|
3763
|
+
(recognizer.state == UIGestureRecognizerStateBegan ||
|
|
3764
|
+
recognizer.state == UIGestureRecognizerStateChanged)) {
|
|
3765
|
+
[self cancelTopInsetReboundCheck];
|
|
3766
|
+
}
|
|
3141
3767
|
if (recognizer == _observedScrollView.panGestureRecognizer) {
|
|
3142
3768
|
[self updateHeaderSettledStateForceBusy:
|
|
3143
3769
|
recognizer.state == UIGestureRecognizerStateBegan || recognizer.state == UIGestureRecognizerStateChanged];
|
|
@@ -3205,7 +3831,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3205
3831
|
[_managedRefreshControls removeObjectForKey:scrollView];
|
|
3206
3832
|
if (current != nil) [_managedRefreshControls setObject:current forKey:scrollView];
|
|
3207
3833
|
}
|
|
3208
|
-
[RNCRefreshControlOffsetState setOffset:_headerHeight + _stickyHeaderHeight
|
|
3834
|
+
[RNCRefreshControlOffsetState setOffset:self.effectiveTopOverlayHeight + _headerHeight + _stickyHeaderHeight
|
|
3209
3835
|
forControl:current owner:self];
|
|
3210
3836
|
}
|
|
3211
3837
|
|
|
@@ -3219,6 +3845,53 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3219
3845
|
[_managedRefreshControls removeObjectForKey:scrollView];
|
|
3220
3846
|
}
|
|
3221
3847
|
|
|
3848
|
+
- (void)cancelTopInsetReboundCheck
|
|
3849
|
+
{
|
|
3850
|
+
[_topInsetReboundTimer invalidate];
|
|
3851
|
+
_topInsetReboundTimer = nil;
|
|
3852
|
+
_pendingTopInsetReboundScrollView = nil;
|
|
3853
|
+
_pendingTopInsetReboundPageKey = nil;
|
|
3854
|
+
}
|
|
3855
|
+
|
|
3856
|
+
- (void)scheduleTopInsetReboundCheck
|
|
3857
|
+
{
|
|
3858
|
+
if (_pendingTopInsetReboundScrollView == nil ||
|
|
3859
|
+
_pendingTopInsetReboundScrollView != _observedScrollView) return;
|
|
3860
|
+
[_topInsetReboundTimer invalidate];
|
|
3861
|
+
__weak __typeof__(self) weakSelf = self;
|
|
3862
|
+
_topInsetReboundTimer = [NSTimer scheduledTimerWithTimeInterval:0.15 repeats:NO block:^(NSTimer *timer) {
|
|
3863
|
+
__strong __typeof__(weakSelf) self = weakSelf;
|
|
3864
|
+
if (self == nil || self->_topInsetReboundTimer != timer) return;
|
|
3865
|
+
self->_topInsetReboundTimer = nil;
|
|
3866
|
+
UIScrollView *scrollView = self->_pendingTopInsetReboundScrollView;
|
|
3867
|
+
if (scrollView == nil || scrollView != self->_observedScrollView ||
|
|
3868
|
+
self->_pendingTopInsetReboundGeneration != self->_generation ||
|
|
3869
|
+
![self->_pendingTopInsetReboundPageKey isEqualToString:self.currentPageKey] ||
|
|
3870
|
+
self->_isBeingRecycled || self.window == nil ||
|
|
3871
|
+
self->_transitioning || self->_isPagerDragging ||
|
|
3872
|
+
self->_headerCommandOwner == scrollView || self->_pagerTopPageKey != nil) {
|
|
3873
|
+
[self cancelTopInsetReboundCheck];
|
|
3874
|
+
return;
|
|
3875
|
+
}
|
|
3876
|
+
UIGestureRecognizerState pan = scrollView.panGestureRecognizer.state;
|
|
3877
|
+
if (scrollView.refreshControl.isRefreshing || scrollView.isDragging ||
|
|
3878
|
+
scrollView.isDecelerating || scrollView.isTracking ||
|
|
3879
|
+
pan == UIGestureRecognizerStateBegan || pan == UIGestureRecognizerStateChanged ||
|
|
3880
|
+
self->_syntheticPageRestore || self->_transitionPageOffsets != nil) {
|
|
3881
|
+
[self scheduleTopInsetReboundCheck];
|
|
3882
|
+
return;
|
|
3883
|
+
}
|
|
3884
|
+
CGFloat minimumOffset = -scrollView.adjustedContentInset.top;
|
|
3885
|
+
[self cancelTopInsetReboundCheck];
|
|
3886
|
+
if (scrollView.contentOffset.y < minimumOffset - 0.5) {
|
|
3887
|
+
// UIKit can finish a refresh rebound at the inset that preceded an overlay resize.
|
|
3888
|
+
self->_correctingTopInsetRebound = YES;
|
|
3889
|
+
[scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x, minimumOffset) animated:NO];
|
|
3890
|
+
self->_correctingTopInsetRebound = NO;
|
|
3891
|
+
}
|
|
3892
|
+
}];
|
|
3893
|
+
}
|
|
3894
|
+
|
|
3222
3895
|
// OneKey patch: release observers, headers and saved state while the scroll view
|
|
3223
3896
|
// still represents the old list, before Fabric applies its next owner's props.
|
|
3224
3897
|
- (void)restoreScrollViewState:(UIScrollView *)scrollView resetSavedOffset:(BOOL)resetSavedOffset
|
|
@@ -3227,9 +3900,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3227
3900
|
[self releaseRefreshControlForScrollView:scrollView];
|
|
3228
3901
|
if (scrollView == _observedScrollView) {
|
|
3229
3902
|
[self detachScrollObserver];
|
|
3230
|
-
[self offsetDiag:resetSavedOffset ? @"release-reset-before" : @"release-retain-before" page:_currentIndex scroll:scrollView];
|
|
3231
3903
|
if (resetSavedOffset && !_nativeSmoothHeaderScrollEnabled) _pageOffsets[self.currentPageKey] = @(_headerOffset);
|
|
3232
|
-
[self offsetDiag:@"release-reset-after" page:_currentIndex scroll:scrollView];
|
|
3233
3904
|
}
|
|
3234
3905
|
if (scrollView == _sharedHeaderScrollView) {
|
|
3235
3906
|
[self restoreSharedHeadersToContainer];
|
|
@@ -3249,10 +3920,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3249
3920
|
[_originalDirectionalLockEnabled objectForKey:scrollView].boolValue;
|
|
3250
3921
|
scrollView.contentInsetAdjustmentBehavior = (UIScrollViewContentInsetAdjustmentBehavior)
|
|
3251
3922
|
[_originalInsetAdjustmentBehavior objectForKey:scrollView].integerValue;
|
|
3252
|
-
[self offsetDiag:@"release-original-offset-before" page:_currentIndex scroll:scrollView];
|
|
3253
3923
|
[scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x, logicalOffset - original.top)
|
|
3254
3924
|
animated:NO];
|
|
3255
|
-
[self offsetDiag:@"release-original-offset-after" page:_currentIndex scroll:scrollView];
|
|
3256
3925
|
[_originalInsets removeObjectForKey:scrollView];
|
|
3257
3926
|
[_appliedTopInsets removeObjectForKey:scrollView];
|
|
3258
3927
|
[_originalIndicatorInsets removeObjectForKey:scrollView];
|
|
@@ -3283,7 +3952,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3283
3952
|
{
|
|
3284
3953
|
// Smooth pages retain their complete offset while replacement identity and
|
|
3285
3954
|
// layout settle. Legacy pages retain the historical header-only policy.
|
|
3286
|
-
[self offsetDiag:restore ? @"restore-enter" : @"insets-enter" page:pageIndex scroll:scrollView];
|
|
3287
3955
|
NSString *pageKey = [self pageKeyForIndex:pageIndex];
|
|
3288
3956
|
RNCCollapsiblePagerReleasedScrollState *released = _releasedPageScrollStates[pageKey];
|
|
3289
3957
|
BOOL deferredReleasedIdentity = NO;
|
|
@@ -3329,13 +3997,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3329
3997
|
deferredReleasedIdentity = now - released.pendingFallbackDetectedAt <
|
|
3330
3998
|
RNCCollapsiblePagerFallbackDebounceInterval;
|
|
3331
3999
|
}
|
|
3332
|
-
#if DEBUG
|
|
3333
|
-
[self offsetDiag:[NSString stringWithFormat:@"released-identity-match=%d-deferred=%d-oldTag=%ld-oldScroll=%p", sameList, deferredReleasedIdentity, (long)released.componentTag, (__bridge void *)released.scrollView] page:pageIndex scroll:scrollView];
|
|
3334
|
-
#endif
|
|
3335
4000
|
if (!deferredReleasedIdentity) {
|
|
3336
|
-
[self offsetDiag:@"released-identity-cache-before" page:pageIndex scroll:scrollView];
|
|
3337
4001
|
if (!sameList && !_nativeSmoothHeaderScrollEnabled) _pageOffsets[pageKey] = @(_headerOffset);
|
|
3338
|
-
[self offsetDiag:@"released-identity-cache-after" page:pageIndex scroll:scrollView];
|
|
3339
4002
|
[_releasedPageScrollStates removeObjectForKey:pageKey];
|
|
3340
4003
|
}
|
|
3341
4004
|
}
|
|
@@ -3360,22 +4023,24 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3360
4023
|
// suppress its later vertical movement while the pager is still pending.
|
|
3361
4024
|
scrollView.directionalLockEnabled = _nativeSmoothHeaderScrollEnabled
|
|
3362
4025
|
? NO : [_originalDirectionalLockEnabled objectForKey:scrollView].boolValue;
|
|
3363
|
-
[self updateNativeScrollerViewport:scrollView stickyHeight:_stickyHeaderHeight];
|
|
4026
|
+
[self updateNativeScrollerViewport:scrollView stickyHeight:_stickyHeaderHeight + self.effectiveTopOverlayHeight];
|
|
3364
4027
|
[self updateRefreshControlForScrollView:scrollView];
|
|
3365
4028
|
CGFloat previousTopInset = scrollView.contentInset.top;
|
|
3366
4029
|
CGFloat previousLogicalOffset = scrollView.contentOffset.y + previousTopInset;
|
|
4030
|
+
// Preserve UIKit's rebound after refresh while the content is beyond its top inset.
|
|
4031
|
+
BOOL wasReturningFromPull = scrollView.contentOffset.y < -scrollView.adjustedContentInset.top - 0.5;
|
|
3367
4032
|
NSNumber *appliedTop = [_appliedTopInsets objectForKey:scrollView];
|
|
3368
4033
|
// UIRefreshControl owns its temporary top inset, including while ending.
|
|
3369
4034
|
CGFloat refreshInset = appliedTop != nil && scrollView.refreshControl != nil
|
|
3370
4035
|
? MAX(0, previousTopInset - appliedTop.doubleValue) : 0;
|
|
3371
4036
|
UIEdgeInsets next = original;
|
|
3372
|
-
next.top += _headerHeight + _stickyHeaderHeight;
|
|
4037
|
+
next.top += self.effectiveTopOverlayHeight + _headerHeight + _stickyHeaderHeight;
|
|
3373
4038
|
CGFloat pagerTopInset = next.top;
|
|
3374
4039
|
next.top += refreshInset;
|
|
3375
4040
|
[_appliedTopInsets setObject:@(pagerTopInset) forKey:scrollView];
|
|
3376
4041
|
// Short pages still need enough range to collapse the header; long pages
|
|
3377
4042
|
// must end at the last row rather than leave a header-sized blank footer.
|
|
3378
|
-
next.bottom += MAX(0, scrollView.bounds.size.height - _stickyHeaderHeight
|
|
4043
|
+
next.bottom += MAX(0, scrollView.bounds.size.height - _stickyHeaderHeight - self.effectiveTopOverlayHeight
|
|
3379
4044
|
- original.top - original.bottom - scrollView.contentSize.height);
|
|
3380
4045
|
if (UIEdgeInsetsEqualToEdgeInsets(scrollView.contentInset, next) && !restore) {
|
|
3381
4046
|
// UICollectionView may report contentSize during a bounce without changing
|
|
@@ -3385,9 +4050,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3385
4050
|
}
|
|
3386
4051
|
BOOL previousSyntheticRestore = _syntheticPageRestore;
|
|
3387
4052
|
if (_nativeSmoothHeaderScrollEnabled || [_alignedBodyKeys containsObject:pageKey]) _syntheticPageRestore = YES;
|
|
3388
|
-
[self offsetDiag:@"inset-assignment-before" page:pageIndex scroll:scrollView];
|
|
3389
4053
|
scrollView.contentInset = next;
|
|
3390
|
-
[self offsetDiag:@"inset-assignment-after" page:pageIndex scroll:scrollView];
|
|
3391
4054
|
UIEdgeInsets indicatorInsets = scrollView.verticalScrollIndicatorInsets;
|
|
3392
4055
|
indicatorInsets.top = pagerTopInset;
|
|
3393
4056
|
scrollView.verticalScrollIndicatorInsets = indicatorInsets;
|
|
@@ -3419,9 +4082,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3419
4082
|
} else {
|
|
3420
4083
|
restoredLogicalOffset = deferredReleasedIdentity || saved == nil ? _headerOffset : saved.doubleValue;
|
|
3421
4084
|
}
|
|
3422
|
-
#if DEBUG
|
|
3423
|
-
[self offsetDiag:[NSString stringWithFormat:@"restore-candidate-saved=%.4f-headerAlignedLogical=%.4f", saved.doubleValue, restoredLogicalOffset] page:pageIndex scroll:scrollView];
|
|
3424
|
-
#endif
|
|
3425
4085
|
intendedLogicalOffset = restoredLogicalOffset;
|
|
3426
4086
|
// Cancellation restores the visible source, while retaining its unreachable reading intent.
|
|
3427
4087
|
if (_nativeSmoothHeaderScrollEnabled && _headerOffset >= _headerHeight &&
|
|
@@ -3434,9 +4094,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3434
4094
|
}
|
|
3435
4095
|
// An inactive list may shrink after its offset observer was detached.
|
|
3436
4096
|
// Do not overwrite its corrected offset with a now-unreachable cached one.
|
|
3437
|
-
#if DEBUG
|
|
3438
|
-
[self offsetDiag:[NSString stringWithFormat:@"restore-clamp-before-candidate=%.4f-max=%.4f", targetOffset, MAX(-next.top, scrollView.contentSize.height - scrollView.bounds.size.height + next.bottom)] page:pageIndex scroll:scrollView];
|
|
3439
|
-
#endif
|
|
3440
4097
|
targetOffset = RNCLimitRestoredScrollOffset(
|
|
3441
4098
|
targetOffset,
|
|
3442
4099
|
scrollView.contentSize.height,
|
|
@@ -3445,13 +4102,17 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3445
4102
|
next.bottom
|
|
3446
4103
|
);
|
|
3447
4104
|
}
|
|
3448
|
-
#if DEBUG
|
|
3449
|
-
[self offsetDiag:[NSString stringWithFormat:@"restore-final-target=%.4f-restore=%d", targetOffset, restore] page:pageIndex scroll:scrollView];
|
|
3450
|
-
#endif
|
|
3451
4105
|
// A bottom-inset change must not cancel UIKit's refresh or bounce settlement.
|
|
3452
|
-
if (restore || previousTopInset != next.top) {
|
|
4106
|
+
if (restore || (previousTopInset != next.top && !wasReturningFromPull)) {
|
|
3453
4107
|
[scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x, targetOffset) animated:NO];
|
|
3454
4108
|
}
|
|
4109
|
+
if (_applyingTopOverlayInsetChange && !restore && wasReturningFromPull &&
|
|
4110
|
+
previousTopInset != next.top && scrollView == _observedScrollView) {
|
|
4111
|
+
_pendingTopInsetReboundScrollView = scrollView;
|
|
4112
|
+
_pendingTopInsetReboundPageKey = [pageKey copy];
|
|
4113
|
+
_pendingTopInsetReboundGeneration = _generation;
|
|
4114
|
+
[self scheduleTopInsetReboundCheck];
|
|
4115
|
+
}
|
|
3455
4116
|
if (_nativeSmoothHeaderScrollEnabled && restore && targetOffset + next.top + 0.5 < intendedLogicalOffset) {
|
|
3456
4117
|
[_pendingPageRestoreKeys addObject:pageKey];
|
|
3457
4118
|
}
|
|
@@ -3461,7 +4122,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3461
4122
|
if (!_nativeSmoothHeaderScrollEnabled) [self updateHeaderForScrollView:scrollView];
|
|
3462
4123
|
else [self applyHeaderOffset];
|
|
3463
4124
|
}
|
|
3464
|
-
[self offsetDiag:@"restore-post" page:pageIndex scroll:scrollView];
|
|
3465
4125
|
}
|
|
3466
4126
|
|
|
3467
4127
|
- (void)updateNativeScrollerViewport:(UIScrollView *)scrollView stickyHeight:(CGFloat)height
|
|
@@ -3478,6 +4138,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3478
4138
|
_observedPageKey == nil || ![_observedPageKey isEqualToString:self.currentPageKey] ||
|
|
3479
4139
|
_observedPageIndex != _currentIndex || _observedPageGeneration != _generation ||
|
|
3480
4140
|
_transitioning || _isPagerDragging || _transitionPageOffsets != nil || _syntheticPageRestore) return;
|
|
4141
|
+
[self cancelTopInsetReboundCheck];
|
|
3481
4142
|
NSInteger phase = [notification.userInfo[@"phase"] integerValue];
|
|
3482
4143
|
[self updateHeaderSettledStateForceBusy:NO];
|
|
3483
4144
|
if (phase == 1) _headerCommandOwner = scrollView;
|
|
@@ -3510,7 +4171,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3510
4171
|
_headerOffset = 0;
|
|
3511
4172
|
[self applyHeaderOffset];
|
|
3512
4173
|
}
|
|
3513
|
-
[self offsetDiag:@"list-command-body-takeover" page:_currentIndex scroll:scrollView];
|
|
3514
4174
|
}
|
|
3515
4175
|
|
|
3516
4176
|
- (void)nativeScrollerWillReceiveScrollCommand:(RNCNativeScrollerComponentView *)scroller
|
|
@@ -3522,12 +4182,12 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3522
4182
|
_observedPageIndex != _currentIndex || _currentIndex < 0 || _currentIndex >= _pageControllers.count ||
|
|
3523
4183
|
_observedPageGeneration != _generation || _transitioning || _isPagerDragging ||
|
|
3524
4184
|
_transitionPageOffsets != nil || _syntheticPageRestore) return;
|
|
4185
|
+
[self cancelTopInsetReboundCheck];
|
|
3525
4186
|
if (!_dispatchingPagerTop) {
|
|
3526
4187
|
[self cancelPagerTop];
|
|
3527
4188
|
[_alignedBodyKeys removeObject:ownerKey];
|
|
3528
4189
|
}
|
|
3529
4190
|
if (!_nativeSmoothHeaderScrollEnabled) return;
|
|
3530
|
-
[self offsetDiag:@"command-pending-cancel-before" page:_observedPageIndex scroll:scrollView];
|
|
3531
4191
|
[_pendingPageRestoreKeys removeObject:ownerKey];
|
|
3532
4192
|
_currentLogicalOffset = scrollView.contentOffset.y + scrollView.contentInset.top;
|
|
3533
4193
|
[_commandBodyKeys removeObject:ownerKey];
|
|
@@ -3536,7 +4196,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3536
4196
|
[_displayBodyOverrides removeObjectForKey:ownerKey];
|
|
3537
4197
|
_pageOffsets[ownerKey] = @(MAX(_currentLogicalOffset - _headerHeight, 0));
|
|
3538
4198
|
[self updateHeaderForScrollView:scrollView];
|
|
3539
|
-
[self offsetDiag:@"command-pending-cancel-after" page:_observedPageIndex scroll:scrollView];
|
|
3540
4199
|
}
|
|
3541
4200
|
|
|
3542
4201
|
|
|
@@ -3598,6 +4257,14 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3598
4257
|
return;
|
|
3599
4258
|
}
|
|
3600
4259
|
if (context == RNCCollapsiblePagerContentOffsetContext && object == _observedScrollView) {
|
|
4260
|
+
if (_pendingTopInsetReboundScrollView == object &&
|
|
4261
|
+
([keyPath isEqualToString:@"contentOffset"] || [keyPath isEqualToString:@"contentInset"])) {
|
|
4262
|
+
if (((UIScrollView *)object).panGestureRecognizer.state == UIGestureRecognizerStateChanged) {
|
|
4263
|
+
[self cancelTopInsetReboundCheck];
|
|
4264
|
+
} else {
|
|
4265
|
+
[self scheduleTopInsetReboundCheck];
|
|
4266
|
+
}
|
|
4267
|
+
}
|
|
3601
4268
|
if (![change[NSKeyValueChangeOldKey] isEqual:change[NSKeyValueChangeNewKey]]) {
|
|
3602
4269
|
[self updateHeaderSettledStateForceBusy:YES];
|
|
3603
4270
|
}
|
|
@@ -3662,18 +4329,12 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3662
4329
|
}
|
|
3663
4330
|
UIScrollView *scrollView = (UIScrollView *)object;
|
|
3664
4331
|
_currentLogicalOffset = scrollView.contentOffset.y + scrollView.contentInset.top;
|
|
3665
|
-
|
|
3666
|
-
BOOL offsetDiagChanged = _pageOffsets[self.currentPageKey] == nil || _pageOffsets[self.currentPageKey].doubleValue != (_nativeSmoothHeaderScrollEnabled ? MAX(_currentLogicalOffset - _headerHeight, 0) : _currentLogicalOffset);
|
|
3667
|
-
if (offsetDiagChanged) [self offsetDiag:@"kvo-cache-before" page:_currentIndex scroll:scrollView];
|
|
3668
|
-
#endif
|
|
4332
|
+
if (_correctingTopInsetRebound) return;
|
|
3669
4333
|
if ([_alignedBodyKeys containsObject:_observedPageKey] ||
|
|
3670
4334
|
(_nativeSmoothHeaderScrollEnabled && scrollView.panGestureRecognizer.state != UIGestureRecognizerStateChanged &&
|
|
3671
4335
|
!scrollView.isDecelerating && ![_commandBodyKeys containsObject:_observedPageKey])) return;
|
|
3672
4336
|
_pageOffsets[_nativeSmoothHeaderScrollEnabled ? _observedPageKey : self.currentPageKey] =
|
|
3673
4337
|
@(_nativeSmoothHeaderScrollEnabled ? MAX(_currentLogicalOffset - _headerHeight, 0) : _currentLogicalOffset);
|
|
3674
|
-
#if DEBUG
|
|
3675
|
-
if (offsetDiagChanged) [self offsetDiag:@"kvo-cache-after" page:_currentIndex scroll:scrollView];
|
|
3676
|
-
#endif
|
|
3677
4338
|
[self updateHeaderForScrollView:scrollView];
|
|
3678
4339
|
return;
|
|
3679
4340
|
}
|
|
@@ -3714,17 +4375,22 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3714
4375
|
? MAX(0, scrollView.contentInset.top - appliedTop.doubleValue) : 0;
|
|
3715
4376
|
CGFloat pinnedTranslation = MAX(0, _currentLogicalOffset - _headerHeight - refreshInset);
|
|
3716
4377
|
CGAffineTransform stickyTransform = CGAffineTransformMakeTranslation(0, pinnedTranslation);
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
4378
|
+
[self updatePinnedStickyHeader];
|
|
4379
|
+
_stickyHeaderView.transform = _stickyHeaderIsPinned ? CGAffineTransformIdentity : stickyTransform;
|
|
4380
|
+
_nativeTabBarView.transform = _stickyHeaderIsPinned ? CGAffineTransformIdentity : stickyTransform;
|
|
4381
|
+
_nativeSubHeaderView.transform = _stickyHeaderIsPinned ? CGAffineTransformIdentity : stickyTransform;
|
|
4382
|
+
[self updateScrollEdgeContentMasks];
|
|
3720
4383
|
return;
|
|
3721
4384
|
}
|
|
4385
|
+
[self updatePinnedStickyHeader];
|
|
3722
4386
|
CGAffineTransform collapseTransform = CGAffineTransformMakeTranslation(0, -_headerOffset);
|
|
3723
4387
|
_sharedHeaderHostView.transform = collapseTransform;
|
|
3724
4388
|
_headerView.transform = CGAffineTransformIdentity;
|
|
3725
4389
|
_stickyHeaderView.transform = CGAffineTransformIdentity;
|
|
3726
4390
|
_nativeTabBarView.transform = CGAffineTransformIdentity;
|
|
3727
4391
|
_nativeSubHeaderView.transform = CGAffineTransformIdentity;
|
|
4392
|
+
[self updateTopScrollEdgeForScrollView:_observedScrollView];
|
|
4393
|
+
[self updateScrollEdgeContentMasks];
|
|
3728
4394
|
}
|
|
3729
4395
|
|
|
3730
4396
|
#pragma mark - Pager navigation
|
|
@@ -3736,7 +4402,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3736
4402
|
|
|
3737
4403
|
- (void)goTo:(NSInteger)index animated:(BOOL)animated
|
|
3738
4404
|
{
|
|
3739
|
-
[self
|
|
4405
|
+
[self cancelTopInsetReboundCheck];
|
|
3740
4406
|
if (_isBeingRecycled || index < 0 || index >= _pageControllers.count) return;
|
|
3741
4407
|
_hasReceivedPageCommand = YES;
|
|
3742
4408
|
_hasAppliedInitialPage = YES;
|
|
@@ -3867,6 +4533,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3867
4533
|
if (!self->_transitioning && !self->_needsSlotRebuild) {
|
|
3868
4534
|
[self attachScrollObserverForCurrentPage];
|
|
3869
4535
|
}
|
|
4536
|
+
[self updateScrollEdgeContentMasks];
|
|
3870
4537
|
});
|
|
3871
4538
|
}
|
|
3872
4539
|
|
|
@@ -3918,7 +4585,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3918
4585
|
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
|
|
3919
4586
|
{
|
|
3920
4587
|
[self updateHeaderSettledStateForceBusy:YES];
|
|
3921
|
-
[self offsetDiag:@"pager-drag-begin" page:_currentIndex scroll:_observedScrollView];
|
|
3922
4588
|
if (_nativeSmoothHeaderScrollEnabled && scrollView == _pagerScrollView) {
|
|
3923
4589
|
[_contentPressCancellationGesture cancelPressForPan:scrollView.panGestureRecognizer];
|
|
3924
4590
|
}
|
|
@@ -4075,6 +4741,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4075
4741
|
CGFloat offset = progress - position;
|
|
4076
4742
|
position = MAX(0, MIN(position, (NSInteger)_pageControllers.count - 1));
|
|
4077
4743
|
[self updatePageStateTransitionProgress:progress];
|
|
4744
|
+
[self updateScrollEdgeContentMasks];
|
|
4078
4745
|
[_nativeTabBarView setProgress:progress];
|
|
4079
4746
|
const auto emitter = [self eventEmitter];
|
|
4080
4747
|
if (emitter) {
|
|
@@ -4093,6 +4760,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4093
4760
|
tabProgress = _currentIndex + (_destinationIndex - _currentIndex) * transitionProgress;
|
|
4094
4761
|
}
|
|
4095
4762
|
[self updatePageStateTransitionProgress:tabProgress];
|
|
4763
|
+
[self updateScrollEdgeContentMasks];
|
|
4096
4764
|
[_nativeTabBarView setProgress:tabProgress];
|
|
4097
4765
|
const auto emitter = [self eventEmitter];
|
|
4098
4766
|
if (emitter) {
|
|
@@ -4110,7 +4778,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4110
4778
|
|
|
4111
4779
|
- (void)emitPageSelected:(NSInteger)position
|
|
4112
4780
|
{
|
|
4113
|
-
[self offsetDiag:@"selection-complete" page:position scroll:_observedScrollView];
|
|
4114
4781
|
const auto emitter = [self eventEmitter];
|
|
4115
4782
|
if (emitter) emitter->onPageSelected({.position = (double)position});
|
|
4116
4783
|
}
|
|
@@ -4255,12 +4922,12 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4255
4922
|
_currentLogicalOffset = logical;
|
|
4256
4923
|
_headerOffset = _pagerTopLogicalTarget;
|
|
4257
4924
|
[self applyHeaderOffset];
|
|
4258
|
-
[self offsetDiag:[NSString stringWithFormat:@"pager-top-complete-token=%lu-target=%.4f", (unsigned long)_pagerTopToken, _pagerTopLogicalTarget] page:_currentIndex scroll:scrollView];
|
|
4259
4925
|
[self cancelPagerTop];
|
|
4260
4926
|
}
|
|
4261
4927
|
|
|
4262
4928
|
- (void)scrollToTop:(NSInteger)target animated:(BOOL)animated
|
|
4263
4929
|
{
|
|
4930
|
+
[self cancelTopInsetReboundCheck];
|
|
4264
4931
|
UIScrollView *scrollView = _observedScrollView;
|
|
4265
4932
|
NSString *key = _observedPageKey;
|
|
4266
4933
|
if ((target != 0 && target != 1) || _isBeingRecycled || _transitioning || _isPagerDragging ||
|
|
@@ -4292,7 +4959,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4292
4959
|
[self applyHeaderOffset];
|
|
4293
4960
|
CGFloat physicalTarget = RNCLimitRestoredScrollOffset(logicalTarget - scrollView.contentInset.top,
|
|
4294
4961
|
scrollView.contentSize.height, scrollView.bounds.size.height, scrollView.contentInset.top, scrollView.contentInset.bottom);
|
|
4295
|
-
[self offsetDiag:[NSString stringWithFormat:@"pager-top-begin-token=%lu-target=%ld-animated=%d", (unsigned long)_pagerTopToken, (long)target, animated] page:_currentIndex scroll:scrollView];
|
|
4296
4962
|
_dispatchingPagerTop = YES;
|
|
4297
4963
|
[scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x, physicalTarget) animated:animated];
|
|
4298
4964
|
_dispatchingPagerTop = NO;
|
|
@@ -4357,6 +5023,22 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4357
5023
|
: velocity;
|
|
4358
5024
|
BOOL hasMotion = MAX(fabs(intent.x), fabs(intent.y)) >= 1;
|
|
4359
5025
|
BOOL horizontal = hasMotion && fabs(intent.x) > fabs(intent.y);
|
|
5026
|
+
if (_sharedHeaderOuterPagerGesture.protectsNestedBody) {
|
|
5027
|
+
NSInteger pageCount = _pageControllers.count;
|
|
5028
|
+
BOOL validPage = _currentIndex >= 0 && _currentIndex < pageCount;
|
|
5029
|
+
NSInteger firstPageIndex = self.isLtrLayout ? 0 : pageCount - 1;
|
|
5030
|
+
NSInteger lastPageIndex = self.isLtrLayout ? pageCount - 1 : 0;
|
|
5031
|
+
BOOL outward = validPage &&
|
|
5032
|
+
((_currentIndex == firstPageIndex && intent.x > 0) ||
|
|
5033
|
+
(_currentIndex == lastPageIndex && intent.x < 0));
|
|
5034
|
+
UIScrollView *outer = RNCNestedOuterReactPagerScrollView(self);
|
|
5035
|
+
BOOL protectInnerPage = horizontal && validPage && pageCount > 1 &&
|
|
5036
|
+
_nestedScrollEnabled && _scrollEnabled && _pagerScrollView.scrollEnabled &&
|
|
5037
|
+
_pagerScrollView.panGestureRecognizer.enabled && outer != nil && outer.scrollEnabled &&
|
|
5038
|
+
[_sharedHeaderOuterPagerGesture.blockedPagerGestures
|
|
5039
|
+
containsObject:outer.panGestureRecognizer] && !outward;
|
|
5040
|
+
return protectInnerPage;
|
|
5041
|
+
}
|
|
4360
5042
|
RNCCollapsiblePagerLog([NSString stringWithFormat:
|
|
4361
5043
|
@"header-axis role=outer-pager-block dx=%.2f dy=%.2f velocity=(%.2f,%.2f) result=%d outer-pagers=%lu",
|
|
4362
5044
|
intent.x,
|
|
@@ -4396,24 +5078,21 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4396
5078
|
return vertical;
|
|
4397
5079
|
}
|
|
4398
5080
|
if (gestureRecognizer != _blockerGesture) return YES;
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
UIPanGestureRecognizer *pan = (UIPanGestureRecognizer *)gestureRecognizer;
|
|
4403
|
-
CGPoint velocity = [pan velocityInView:self];
|
|
5081
|
+
UIPanGestureRecognizer *pan = [gestureRecognizer isKindOfClass:UIPanGestureRecognizer.class]
|
|
5082
|
+
? (UIPanGestureRecognizer *)gestureRecognizer : nil;
|
|
5083
|
+
CGPoint velocity = pan != nil ? [pan velocityInView:self] : CGPointZero;
|
|
4404
5084
|
NSInteger pageCount = _pageControllers.count;
|
|
4405
|
-
if (pageCount == 0 || _currentIndex < 0 || _currentIndex >= pageCount) return NO;
|
|
4406
|
-
|
|
4407
5085
|
CGFloat absVelocityX = fabs(velocity.x);
|
|
4408
5086
|
CGFloat absVelocityY = fabs(velocity.y);
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
BOOL
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
5087
|
+
NSInteger firstPageIndex = self.isLtrLayout ? 0 : pageCount - 1;
|
|
5088
|
+
NSInteger lastPageIndex = self.isLtrLayout ? pageCount - 1 : 0;
|
|
5089
|
+
BOOL validPage = pageCount > 0 && _currentIndex >= 0 && _currentIndex < pageCount;
|
|
5090
|
+
BOOL horizontal = absVelocityX > absVelocityY && absVelocityX >= 1.0f;
|
|
5091
|
+
BOOL outward = validPage &&
|
|
5092
|
+
((_currentIndex == firstPageIndex && velocity.x > 0) ||
|
|
5093
|
+
(_currentIndex == lastPageIndex && velocity.x < 0));
|
|
5094
|
+
BOOL shouldBegin = _nestedScrollEnabled && pan != nil && horizontal && outward;
|
|
5095
|
+
return shouldBegin;
|
|
4417
5096
|
}
|
|
4418
5097
|
|
|
4419
5098
|
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
|
|
@@ -4423,13 +5102,29 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4423
5102
|
_sharedHeaderPressCancellationGestureHost == nil) return NO;
|
|
4424
5103
|
UIView *touchView = touch.view;
|
|
4425
5104
|
BOOL beginsInSharedHeader = touchView == _sharedHeaderHostView ||
|
|
4426
|
-
[touchView isDescendantOfView:_sharedHeaderHostView]
|
|
4427
|
-
|
|
5105
|
+
[touchView isDescendantOfView:_sharedHeaderHostView] ||
|
|
5106
|
+
touchView == _pinnedStickyHostView ||
|
|
5107
|
+
[touchView isDescendantOfView:_pinnedStickyHostView];
|
|
5108
|
+
if (_sharedHeaderOuterPagerGesture.trackedTouch != nil &&
|
|
5109
|
+
_sharedHeaderOuterPagerGesture.trackedTouch != touch) return NO;
|
|
5110
|
+
UIScrollView *outer = RNCNestedOuterReactPagerScrollView(self);
|
|
5111
|
+
BOOL beginsInNestedBody = !beginsInSharedHeader && _nestedScrollEnabled &&
|
|
5112
|
+
_scrollEnabled && _pagerScrollView.scrollEnabled &&
|
|
5113
|
+
_pagerScrollView.panGestureRecognizer.enabled && _pageControllers.count > 1 &&
|
|
5114
|
+
outer != nil && outer.scrollEnabled &&
|
|
5115
|
+
[_sharedHeaderOuterPagerGesture.blockedPagerGestures containsObject:outer.panGestureRecognizer] &&
|
|
5116
|
+
(touchView == _pagerScrollView || [touchView isDescendantOfView:_pagerScrollView]);
|
|
5117
|
+
if (!beginsInSharedHeader && !beginsInNestedBody) return NO;
|
|
5118
|
+
|
|
5119
|
+
_sharedHeaderOuterPagerGesture.trackedTouch = touch;
|
|
5120
|
+
_sharedHeaderOuterPagerGesture.protectsNestedBody = beginsInNestedBody;
|
|
5121
|
+
_sharedHeaderOuterPagerGesture.cancelsTouchesInView = !beginsInNestedBody;
|
|
4428
5122
|
|
|
4429
5123
|
RNCCollapsiblePagerLog([NSString stringWithFormat:
|
|
4430
|
-
@"outer-pager-touch target=%@ outer-pagers=%lu",
|
|
5124
|
+
@"outer-pager-touch target=%@ outer-pagers=%lu nested-body=%d",
|
|
4431
5125
|
touchView == nil ? @"none" : NSStringFromClass(touchView.class),
|
|
4432
|
-
(unsigned long)_sharedHeaderOuterPagerGesture.blockedPagerGestures.count
|
|
5126
|
+
(unsigned long)_sharedHeaderOuterPagerGesture.blockedPagerGestures.count,
|
|
5127
|
+
beginsInNestedBody]);
|
|
4433
5128
|
return YES;
|
|
4434
5129
|
}
|
|
4435
5130
|
if (gestureRecognizer == _contentPressCancellationGesture) {
|
|
@@ -4438,14 +5133,17 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4438
5133
|
return _nativeSmoothHeaderScrollEnabled && !_isBeingRecycled &&
|
|
4439
5134
|
_contentPressCancellationGesture.view != nil &&
|
|
4440
5135
|
[touchView isDescendantOfView:_pagerScrollView] &&
|
|
4441
|
-
![touchView isDescendantOfView:_sharedHeaderHostView]
|
|
5136
|
+
![touchView isDescendantOfView:_sharedHeaderHostView] &&
|
|
5137
|
+
![touchView isDescendantOfView:_pinnedStickyHostView];
|
|
4442
5138
|
}
|
|
4443
5139
|
if (gestureRecognizer == _sharedHeaderPressCancellationGesture) {
|
|
4444
5140
|
if (!_nativeSmoothHeaderScrollEnabled || _isBeingRecycled ||
|
|
4445
5141
|
_sharedHeaderPressCancellationGestureHost == nil) return NO;
|
|
4446
5142
|
UIView *touchView = touch.view;
|
|
4447
5143
|
BOOL beginsInSharedHeader = touchView == _sharedHeaderHostView ||
|
|
4448
|
-
[touchView isDescendantOfView:_sharedHeaderHostView]
|
|
5144
|
+
[touchView isDescendantOfView:_sharedHeaderHostView] ||
|
|
5145
|
+
touchView == _pinnedStickyHostView ||
|
|
5146
|
+
[touchView isDescendantOfView:_pinnedStickyHostView];
|
|
4449
5147
|
if (!beginsInSharedHeader) return NO;
|
|
4450
5148
|
|
|
4451
5149
|
RNCCollapsiblePagerLog([NSString stringWithFormat:
|
|
@@ -4456,7 +5154,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4456
5154
|
if (gestureRecognizer == _verticalPagerGesture) {
|
|
4457
5155
|
UIView *touchView = touch.view;
|
|
4458
5156
|
return touchView != _sharedHeaderHostView &&
|
|
4459
|
-
![touchView isDescendantOfView:_sharedHeaderHostView]
|
|
5157
|
+
![touchView isDescendantOfView:_sharedHeaderHostView] &&
|
|
5158
|
+
touchView != _pinnedStickyHostView &&
|
|
5159
|
+
![touchView isDescendantOfView:_pinnedStickyHostView];
|
|
4460
5160
|
}
|
|
4461
5161
|
// A new touch can cancel UIKit's animation before its first frame. Queue a
|
|
4462
5162
|
// non-animated settle for the next safe transition boundary instead of
|