@onekeyfe/react-native-pager-view 3.0.177 → 3.0.179-alpha.20261015
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 +75 -1
- package/ios/RNCCollapsiblePagerViewComponentView.mm +835 -138
- package/ios/RNCNativeScrollerComponentView.mm +93 -38
- package/ios/RNCPagerViewComponentView.h +7 -0
- package/ios/RNCPagerViewComponentView.mm +34 -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,319 @@ 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
|
+
(_externalOuterPageActive && !_externalOuterTransitioning);
|
|
2054
|
+
BOOL hasOverlay = _topScrollEdgeHard && self.hasFixedTopOverlay &&
|
|
2055
|
+
externalOwnerReady && self.window != nil;
|
|
2056
|
+
BOOL hasSticky = _topScrollEdgeHard && _stickyHeaderView != nil &&
|
|
2057
|
+
externalOwnerReady && self.window != nil;
|
|
2058
|
+
BOOL enabled = (hasOverlay || hasSticky) && scrollView != nil;
|
|
2059
|
+
BOOL topHostChanged = (_topEdgeInteraction != nil) != hasOverlay ||
|
|
2060
|
+
(hasOverlay && _topEdgeInteractionHost != _topOverlayHostView);
|
|
2061
|
+
if (_topEdgeScrollView != nil && (!enabled || _topEdgeScrollView != scrollView ||
|
|
2062
|
+
topHostChanged)) {
|
|
2063
|
+
[_topEdgeInteractionHost removeInteraction:_topEdgeInteraction];
|
|
2064
|
+
_topEdgeInteraction.scrollView = nil;
|
|
2065
|
+
[_stickyEdgeInteractionHost removeInteraction:_stickyEdgeInteraction];
|
|
2066
|
+
_stickyEdgeInteraction.scrollView = nil;
|
|
2067
|
+
[_topEdgeMarker removeFromSuperview];
|
|
2068
|
+
[_stickyEdgeMarker removeFromSuperview];
|
|
2069
|
+
_topEdgeScrollView.topEdgeEffect.style = _originalTopEdgeStyle;
|
|
2070
|
+
_topEdgeInteraction = nil;
|
|
2071
|
+
_stickyEdgeInteraction = nil;
|
|
2072
|
+
_topEdgeMarker = nil;
|
|
2073
|
+
_stickyEdgeMarker = nil;
|
|
2074
|
+
_topEdgeInteractionHost = nil;
|
|
2075
|
+
_stickyEdgeInteractionHost = nil;
|
|
2076
|
+
_topEdgeScrollView = nil;
|
|
2077
|
+
_originalTopEdgeStyle = nil;
|
|
2078
|
+
}
|
|
2079
|
+
if (enabled && _topEdgeScrollView == nil) {
|
|
2080
|
+
_originalTopEdgeStyle = scrollView.topEdgeEffect.style;
|
|
2081
|
+
_topEdgeScrollView = scrollView;
|
|
2082
|
+
scrollView.topEdgeEffect.style = UIScrollEdgeEffectStyle.hardStyle;
|
|
2083
|
+
}
|
|
2084
|
+
if (hasOverlay && enabled && _topEdgeInteraction == nil) {
|
|
2085
|
+
// UIKit needs a native element to shape the edge effect around React-drawn content.
|
|
2086
|
+
_topEdgeMarker = [UILabel new];
|
|
2087
|
+
_topEdgeMarker.frame = _topOverlayHostView.bounds;
|
|
2088
|
+
_topEdgeMarker.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
2089
|
+
_topEdgeMarker.text = @" ";
|
|
2090
|
+
_topEdgeMarker.textColor = UIColor.clearColor;
|
|
2091
|
+
_topEdgeMarker.backgroundColor = UIColor.clearColor;
|
|
2092
|
+
_topEdgeMarker.userInteractionEnabled = NO;
|
|
2093
|
+
_topEdgeMarker.isAccessibilityElement = NO;
|
|
2094
|
+
_topEdgeMarker.accessibilityElementsHidden = YES;
|
|
2095
|
+
[_topOverlayHostView addSubview:_topEdgeMarker];
|
|
2096
|
+
_topEdgeInteraction = [UIScrollEdgeElementContainerInteraction new];
|
|
2097
|
+
_topEdgeInteraction.edge = UIRectEdgeTop;
|
|
2098
|
+
_topEdgeInteraction.scrollView = scrollView;
|
|
2099
|
+
[_topOverlayHostView addInteraction:_topEdgeInteraction];
|
|
2100
|
+
_topEdgeInteractionHost = _topOverlayHostView;
|
|
2101
|
+
}
|
|
2102
|
+
BOOL legacyStickyPinned = !_nativeSmoothHeaderScrollEnabled &&
|
|
2103
|
+
_stickyHeaderView != nil && _headerOffset >= _headerHeight - 0.5;
|
|
2104
|
+
BOOL stickyEnabled = enabled && (_stickyHeaderIsPinned || legacyStickyPinned);
|
|
2105
|
+
UIView *stickyInteractionHost = _stickyHeaderIsPinned
|
|
2106
|
+
? _pinnedStickyHostView : _stickyHeaderView;
|
|
2107
|
+
if (_stickyEdgeInteractionHost != nil &&
|
|
2108
|
+
(!stickyEnabled || _stickyEdgeInteractionHost != stickyInteractionHost)) {
|
|
2109
|
+
[_stickyEdgeInteractionHost removeInteraction:_stickyEdgeInteraction];
|
|
2110
|
+
_stickyEdgeInteraction.scrollView = nil;
|
|
2111
|
+
[_stickyEdgeMarker removeFromSuperview];
|
|
2112
|
+
_stickyEdgeMarker = nil;
|
|
2113
|
+
_stickyEdgeInteraction = nil;
|
|
2114
|
+
_stickyEdgeInteractionHost = nil;
|
|
2115
|
+
}
|
|
2116
|
+
if (stickyEnabled && _stickyEdgeInteraction == nil) {
|
|
2117
|
+
_stickyEdgeMarker = [UILabel new];
|
|
2118
|
+
_stickyEdgeMarker.frame = stickyInteractionHost.bounds;
|
|
2119
|
+
_stickyEdgeMarker.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
2120
|
+
_stickyEdgeMarker.text = @" ";
|
|
2121
|
+
_stickyEdgeMarker.textColor = UIColor.clearColor;
|
|
2122
|
+
_stickyEdgeMarker.backgroundColor = UIColor.clearColor;
|
|
2123
|
+
_stickyEdgeMarker.userInteractionEnabled = NO;
|
|
2124
|
+
_stickyEdgeMarker.isAccessibilityElement = NO;
|
|
2125
|
+
_stickyEdgeMarker.accessibilityElementsHidden = YES;
|
|
2126
|
+
[stickyInteractionHost addSubview:_stickyEdgeMarker];
|
|
2127
|
+
_stickyEdgeInteraction = [UIScrollEdgeElementContainerInteraction new];
|
|
2128
|
+
_stickyEdgeInteraction.edge = UIRectEdgeTop;
|
|
2129
|
+
_stickyEdgeInteraction.scrollView = scrollView;
|
|
2130
|
+
[stickyInteractionHost addInteraction:_stickyEdgeInteraction];
|
|
2131
|
+
_stickyEdgeInteractionHost = stickyInteractionHost;
|
|
2132
|
+
}
|
|
2133
|
+
[self updateScrollEdgeContentMasks];
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
|
|
1715
2137
|
- (void)dealloc
|
|
1716
2138
|
{
|
|
2139
|
+
[self updateTopScrollEdgeForScrollView:nil];
|
|
1717
2140
|
[self cancelHeaderSettledCheck];
|
|
1718
2141
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"OneKeyNativeListScrollCommandAccepted" object:nil];
|
|
1719
2142
|
for (UIScrollView *scrollView in _originalInsets.keyEnumerator) {
|
|
@@ -1766,6 +2189,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1766
2189
|
_hasPendingDirectNativePagerChange = NO;
|
|
1767
2190
|
if (_directNativePagerEnabled == enabled) return;
|
|
1768
2191
|
|
|
2192
|
+
[self restoreScrollEdgeContentMasks];
|
|
1769
2193
|
[self detachScrollObserver];
|
|
1770
2194
|
[self restoreSharedHeadersToContainer];
|
|
1771
2195
|
if (_blockerGesture != nil) {
|
|
@@ -1841,8 +2265,33 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1841
2265
|
|
|
1842
2266
|
#pragma mark - Smooth shared header ownership
|
|
1843
2267
|
|
|
2268
|
+
- (void)restorePinnedStickyHeader
|
|
2269
|
+
{
|
|
2270
|
+
if (!_stickyHeaderIsPinned) return;
|
|
2271
|
+
_stickyHeaderIsPinned = NO;
|
|
2272
|
+
_stickyHeaderView.transform = CGAffineTransformIdentity;
|
|
2273
|
+
_nativeTabBarView.transform = CGAffineTransformIdentity;
|
|
2274
|
+
_nativeSubHeaderView.transform = CGAffineTransformIdentity;
|
|
2275
|
+
[_stickyHeaderView removeFromSuperview];
|
|
2276
|
+
[_nativeTabBarView removeFromSuperview];
|
|
2277
|
+
[_nativeSubHeaderView removeFromSuperview];
|
|
2278
|
+
if (_stickyHeaderView != nil) [_sharedHeaderHostView addSubview:_stickyHeaderView];
|
|
2279
|
+
[_sharedHeaderHostView addSubview:_nativeTabBarView];
|
|
2280
|
+
[_sharedHeaderHostView addSubview:_nativeSubHeaderView];
|
|
2281
|
+
_pinnedStickyHostView.hidden = YES;
|
|
2282
|
+
_pinnedStickyHostView.layer.zPosition = 0;
|
|
2283
|
+
[_containerView addSubview:_pinnedStickyHostView];
|
|
2284
|
+
if ([_pagerScrollView isKindOfClass:RNCCollapsiblePagerDirectScrollView.class]) {
|
|
2285
|
+
((RNCCollapsiblePagerDirectScrollView *)_pagerScrollView).excludedHeaderView = _sharedHeaderHostView;
|
|
2286
|
+
}
|
|
2287
|
+
[_nativeTabBarView updateAncestorGesturePrecedence];
|
|
2288
|
+
[_nativeSubHeaderView updateAncestorGesturePrecedence];
|
|
2289
|
+
}
|
|
2290
|
+
|
|
1844
2291
|
- (void)restoreSharedHeadersToContainer
|
|
1845
2292
|
{
|
|
2293
|
+
[self restorePinnedStickyHeader];
|
|
2294
|
+
[self updateTopScrollEdgeForScrollView:_observedScrollView];
|
|
1846
2295
|
_sharedHeaderScrollView = nil;
|
|
1847
2296
|
_sharedHeadersLiftedForPagerTransition = NO;
|
|
1848
2297
|
_sharedHeaderHostView.layer.zPosition = 0;
|
|
@@ -1850,11 +2299,15 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1850
2299
|
[_containerView addSubview:_sharedHeaderHostView];
|
|
1851
2300
|
}
|
|
1852
2301
|
[self layoutSharedHeaders];
|
|
2302
|
+
if (self.hasFixedTopOverlay) {
|
|
2303
|
+
[_containerView bringSubviewToFront:_topOverlayHostView];
|
|
2304
|
+
}
|
|
1853
2305
|
}
|
|
1854
2306
|
|
|
1855
2307
|
- (void)attachSharedHeadersToScrollView:(UIScrollView *)scrollView
|
|
1856
2308
|
{
|
|
1857
2309
|
if (!_nativeSmoothHeaderScrollEnabled || _transitioning || _isPagerDragging ||
|
|
2310
|
+
_externalOuterTransitioning ||
|
|
1858
2311
|
scrollView == nil || _isBeingRecycled) return;
|
|
1859
2312
|
if ((UIView *)scrollView == _sharedHeaderHostView ||
|
|
1860
2313
|
[scrollView isDescendantOfView:_sharedHeaderHostView]) {
|
|
@@ -1866,13 +2319,16 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1866
2319
|
}
|
|
1867
2320
|
if (_sharedHeaderScrollView == scrollView &&
|
|
1868
2321
|
_sharedHeaderHostView.superview == scrollView) {
|
|
2322
|
+
_sharedHeaderHostView.layer.zPosition =
|
|
2323
|
+
_topScrollEdgeHard && (self.hasFixedTopOverlay || _stickyHeaderView != nil) ? 0 : 1000;
|
|
1869
2324
|
[self layoutSharedHeaders];
|
|
1870
2325
|
return;
|
|
1871
2326
|
}
|
|
1872
2327
|
|
|
1873
2328
|
_sharedHeaderScrollView = scrollView;
|
|
1874
2329
|
_sharedHeadersLiftedForPagerTransition = NO;
|
|
1875
|
-
_sharedHeaderHostView.layer.zPosition =
|
|
2330
|
+
_sharedHeaderHostView.layer.zPosition =
|
|
2331
|
+
_topScrollEdgeHard && (self.hasFixedTopOverlay || _stickyHeaderView != nil) ? 0 : 1000;
|
|
1876
2332
|
if (_sharedHeaderHostView.superview != scrollView) {
|
|
1877
2333
|
[scrollView addSubview:_sharedHeaderHostView];
|
|
1878
2334
|
}
|
|
@@ -1919,56 +2375,114 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1919
2375
|
|
|
1920
2376
|
_sharedHeaderHostView.frame = CGRectMake(
|
|
1921
2377
|
0,
|
|
1922
|
-
attachedToList ? -totalHeight :
|
|
2378
|
+
attachedToList ? -totalHeight : self.effectiveTopOverlayHeight,
|
|
1923
2379
|
width,
|
|
1924
2380
|
totalHeight
|
|
1925
2381
|
);
|
|
1926
2382
|
CGFloat stickyY = _headerHeight;
|
|
1927
2383
|
_headerView.frame = CGRectMake(0, 0, width, _headerHeight);
|
|
1928
|
-
|
|
2384
|
+
CGPoint pinnedOrigin = _stickyHeaderIsPinned
|
|
2385
|
+
? [_sharedHeaderScrollView convertPoint:CGPointMake(0, self.effectiveTopOverlayHeight) fromView:_containerView]
|
|
2386
|
+
: CGPointMake(0, self.effectiveTopOverlayHeight);
|
|
2387
|
+
_pinnedStickyHostView.frame = CGRectMake(
|
|
2388
|
+
pinnedOrigin.x, pinnedOrigin.y, width, _stickyHeaderHeight);
|
|
2389
|
+
CGFloat stickyChildY = _stickyHeaderIsPinned ? 0 : stickyY;
|
|
2390
|
+
_stickyHeaderView.frame = CGRectMake(0, stickyChildY, width, _stickyHeaderHeight);
|
|
1929
2391
|
CGFloat nativeTabBarVisibleHeight = _nativeTabBarView.hidden
|
|
1930
2392
|
? 0
|
|
1931
2393
|
: MIN(_stickyHeaderHeight, _nativeTabBarHeight);
|
|
1932
2394
|
_nativeTabBarView.frame = CGRectMake(
|
|
1933
2395
|
0,
|
|
1934
|
-
|
|
2396
|
+
stickyChildY,
|
|
1935
2397
|
width,
|
|
1936
2398
|
nativeTabBarVisibleHeight
|
|
1937
2399
|
);
|
|
1938
2400
|
_nativeSubHeaderView.frame = CGRectMake(
|
|
1939
2401
|
0,
|
|
1940
|
-
|
|
2402
|
+
stickyChildY + nativeTabBarVisibleHeight,
|
|
1941
2403
|
width,
|
|
1942
2404
|
MIN(
|
|
1943
2405
|
MAX(0, _stickyHeaderHeight - nativeTabBarVisibleHeight),
|
|
1944
2406
|
_nativeSubHeaderHeight
|
|
1945
2407
|
)
|
|
1946
2408
|
);
|
|
2409
|
+
_topOverlayHostView.frame = CGRectMake(0, 0, width, self.effectiveTopOverlayHeight);
|
|
2410
|
+
_topOverlayView.frame = _topOverlayHostView.bounds;
|
|
1947
2411
|
[self applyHeaderOffset];
|
|
1948
2412
|
}
|
|
1949
2413
|
|
|
1950
|
-
- (void)
|
|
2414
|
+
- (void)updatePinnedStickyHeader
|
|
1951
2415
|
{
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
if (!
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
(
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
2416
|
+
BOOL attachedToList = _nativeSmoothHeaderScrollEnabled &&
|
|
2417
|
+
_sharedHeaderScrollView != nil && !_sharedHeadersLiftedForPagerTransition;
|
|
2418
|
+
if (!attachedToList && !_stickyHeaderIsPinned) return;
|
|
2419
|
+
UIScrollView *scrollView = _sharedHeaderScrollView;
|
|
2420
|
+
NSNumber *appliedTop = [_appliedTopInsets objectForKey:scrollView];
|
|
2421
|
+
CGFloat refreshInset = scrollView.refreshControl != nil && appliedTop != nil
|
|
2422
|
+
? MAX(0, scrollView.contentInset.top - appliedTop.doubleValue) : 0;
|
|
2423
|
+
CGFloat pinnedTranslation = MAX(0, _currentLogicalOffset - _headerHeight - refreshInset);
|
|
2424
|
+
CGFloat stickyTop = [_sharedHeaderHostView convertPoint:
|
|
2425
|
+
CGPointMake(0, _headerHeight + pinnedTranslation) toView:self].y;
|
|
2426
|
+
BOOL shouldPin = _topScrollEdgeHard &&
|
|
2427
|
+
_stickyHeaderView != nil && attachedToList && !_transitioning &&
|
|
2428
|
+
!_externalOuterTransitioning &&
|
|
2429
|
+
!_isPagerDragging && _headerOffset >= _headerHeight - 0.5 &&
|
|
2430
|
+
stickyTop <= self.effectiveTopOverlayHeight + 0.5;
|
|
2431
|
+
if (_stickyHeaderIsPinned && shouldPin && _pinnedStickyHostView.superview != scrollView) {
|
|
2432
|
+
[self updateTopScrollEdgeForScrollView:nil];
|
|
2433
|
+
[scrollView addSubview:_pinnedStickyHostView];
|
|
2434
|
+
[self updateTopScrollEdgeForScrollView:_observedScrollView];
|
|
2435
|
+
}
|
|
2436
|
+
if (_stickyHeaderIsPinned == shouldPin) {
|
|
2437
|
+
if (shouldPin) {
|
|
2438
|
+
CGPoint origin = [scrollView convertPoint:CGPointMake(0, self.effectiveTopOverlayHeight)
|
|
2439
|
+
fromView:_containerView];
|
|
2440
|
+
_pinnedStickyHostView.frame = CGRectMake(origin.x, origin.y,
|
|
2441
|
+
CGRectGetWidth(self.bounds), _stickyHeaderHeight);
|
|
2442
|
+
}
|
|
2443
|
+
return;
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2446
|
+
_stickyHeaderIsPinned = shouldPin;
|
|
2447
|
+
UIView *host = shouldPin ? _pinnedStickyHostView : _sharedHeaderHostView;
|
|
2448
|
+
_stickyHeaderView.transform = CGAffineTransformIdentity;
|
|
2449
|
+
_nativeTabBarView.transform = CGAffineTransformIdentity;
|
|
2450
|
+
_nativeSubHeaderView.transform = CGAffineTransformIdentity;
|
|
2451
|
+
[_stickyHeaderView removeFromSuperview];
|
|
2452
|
+
[_nativeTabBarView removeFromSuperview];
|
|
2453
|
+
[_nativeSubHeaderView removeFromSuperview];
|
|
2454
|
+
if (_stickyHeaderView != nil) [host addSubview:_stickyHeaderView];
|
|
2455
|
+
[host addSubview:_nativeTabBarView];
|
|
2456
|
+
[host addSubview:_nativeSubHeaderView];
|
|
2457
|
+
CGFloat childY = shouldPin ? 0 : _headerHeight;
|
|
2458
|
+
CGFloat width = CGRectGetWidth(self.bounds);
|
|
2459
|
+
CGFloat tabHeight = _nativeTabBarView.hidden ? 0 : MIN(_stickyHeaderHeight, _nativeTabBarHeight);
|
|
2460
|
+
_stickyHeaderView.frame = CGRectMake(0, childY, width, _stickyHeaderHeight);
|
|
2461
|
+
_nativeTabBarView.frame = CGRectMake(0, childY, width, tabHeight);
|
|
2462
|
+
_nativeSubHeaderView.frame = CGRectMake(0, childY + tabHeight, width,
|
|
2463
|
+
MIN(MAX(0, _stickyHeaderHeight - tabHeight), _nativeSubHeaderHeight));
|
|
2464
|
+
_pinnedStickyHostView.hidden = !shouldPin;
|
|
2465
|
+
if ([_pagerScrollView isKindOfClass:RNCCollapsiblePagerDirectScrollView.class]) {
|
|
2466
|
+
((RNCCollapsiblePagerDirectScrollView *)_pagerScrollView).excludedHeaderView =
|
|
2467
|
+
shouldPin ? _pinnedStickyHostView : _sharedHeaderHostView;
|
|
2468
|
+
}
|
|
2469
|
+
if (shouldPin) {
|
|
2470
|
+
[scrollView addSubview:_pinnedStickyHostView];
|
|
2471
|
+
_pinnedStickyHostView.layer.zPosition = 1000;
|
|
2472
|
+
CGPoint origin = [scrollView convertPoint:CGPointMake(0, self.effectiveTopOverlayHeight)
|
|
2473
|
+
fromView:_containerView];
|
|
2474
|
+
_pinnedStickyHostView.frame = CGRectMake(origin.x, origin.y, width, _stickyHeaderHeight);
|
|
2475
|
+
} else {
|
|
2476
|
+
_pinnedStickyHostView.layer.zPosition = 0;
|
|
2477
|
+
[_containerView addSubview:_pinnedStickyHostView];
|
|
2478
|
+
}
|
|
2479
|
+
[_nativeTabBarView updateAncestorGesturePrecedence];
|
|
2480
|
+
[_nativeSubHeaderView updateAncestorGesturePrecedence];
|
|
2481
|
+
[self updateTopScrollEdgeForScrollView:_observedScrollView];
|
|
1967
2482
|
}
|
|
1968
2483
|
|
|
1969
2484
|
- (void)preparePageForHorizontalTransitionAtIndex:(NSInteger)pageIndex
|
|
1970
2485
|
{
|
|
1971
|
-
[self offsetDiag:@"prepare-check" page:pageIndex scroll:nil];
|
|
1972
2486
|
if (!_nativeSmoothHeaderScrollEnabled ||
|
|
1973
2487
|
pageIndex < 0 || pageIndex >= _pageControllers.count) return;
|
|
1974
2488
|
UIScrollView *scrollView = [self verticalScrollViewForPageAtIndex:pageIndex];
|
|
@@ -1987,11 +2501,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1987
2501
|
_transitionHeaderOffset = _headerOffset;
|
|
1988
2502
|
_transitionPageOffsets = [_pageOffsets copy];
|
|
1989
2503
|
_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
2504
|
}
|
|
1996
2505
|
|
|
1997
2506
|
- (void)updatePageStateTransitionProgress:(CGFloat)progress
|
|
@@ -2008,16 +2517,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2008
2517
|
CGFloat firstQ = [_transitionPageOffsets[[self pageKeyForIndex:first]] doubleValue];
|
|
2009
2518
|
CGFloat secondQ = [_transitionPageOffsets[[self pageKeyForIndex:second]] doubleValue];
|
|
2010
2519
|
_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
2520
|
_syntheticPageRestore = YES;
|
|
2022
2521
|
NSInteger visibleFirst = MAX(0, MIN((NSInteger)floor(progress), (NSInteger)_pageControllers.count - 1));
|
|
2023
2522
|
NSInteger visibleLast = MIN(visibleFirst + 1, (NSInteger)_pageControllers.count - 1);
|
|
@@ -2059,10 +2558,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2059
2558
|
_transitionPageOffsets = nil;
|
|
2060
2559
|
_transitionCompensatedPages = nil;
|
|
2061
2560
|
[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
2561
|
}
|
|
2067
2562
|
|
|
2068
2563
|
#pragma mark - React mounting
|
|
@@ -2119,9 +2614,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2119
2614
|
if (!_nativeSmoothHeaderScrollEnabled && _transitionPageOffsets == nil && !_syntheticPageRestore &&
|
|
2120
2615
|
![_pendingPageRestoreKeys containsObject:pageKey] &&
|
|
2121
2616
|
(ownsObservedPage || _pageOffsets[pageKey] == nil)) {
|
|
2122
|
-
[self offsetDiag:@"mount-release-save-before" page:index scroll:scrollView];
|
|
2123
2617
|
_pageOffsets[pageKey] = @(scrollView.contentOffset.y + scrollView.contentInset.top);
|
|
2124
|
-
[self offsetDiag:@"mount-release-save-after" page:index scroll:scrollView];
|
|
2125
2618
|
}
|
|
2126
2619
|
break;
|
|
2127
2620
|
}
|
|
@@ -2137,6 +2630,27 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2137
2630
|
- (void)mountingTransactionDidMount:(const MountingTransaction &)transaction
|
|
2138
2631
|
withSurfaceTelemetry:(const SurfaceTelemetry &)surfaceTelemetry
|
|
2139
2632
|
{
|
|
2633
|
+
if (_needsSlotRebuild && !_deferTopEdgeSlotRebuild &&
|
|
2634
|
+
!_transitioning && !_isPagerDragging) {
|
|
2635
|
+
[self rebuildSlots];
|
|
2636
|
+
}
|
|
2637
|
+
[self registerExternalOuterPager];
|
|
2638
|
+
if ([self resolveExternalTopOverlay]) {
|
|
2639
|
+
[self reapplyInsetsToObservedScrollView];
|
|
2640
|
+
[self setNeedsLayout];
|
|
2641
|
+
}
|
|
2642
|
+
if (_pendingTopOverlayAppearanceCommit) {
|
|
2643
|
+
_pendingTopOverlayAppearanceCommit = NO;
|
|
2644
|
+
BOOL localOverlayReady = _topOverlayView.superview == _topOverlayHostView &&
|
|
2645
|
+
_topOverlayView.window == self.window;
|
|
2646
|
+
BOOL externalOverlayReady = _externalTopOverlayTag > 0 &&
|
|
2647
|
+
_externalTopOverlayView.window == self.window;
|
|
2648
|
+
if (!_isBeingRecycled && _topScrollEdgeHard && self.window != nil &&
|
|
2649
|
+
(localOverlayReady || externalOverlayReady)) {
|
|
2650
|
+
[self updateTopScrollEdgeForScrollView:_observedScrollView];
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
[self updateScrollEdgeContentMasks];
|
|
2140
2654
|
[_mountingRemovedScrollViews removeAllObjects];
|
|
2141
2655
|
if (!_nativeSmoothHeaderScrollEnabled || _isBeingRecycled || self.window == nil ||
|
|
2142
2656
|
_currentIndex < 0 || _currentIndex >= _pageControllers.count) return;
|
|
@@ -2214,7 +2728,11 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2214
2728
|
if (_isBeingRecycled) return;
|
|
2215
2729
|
NSInteger safeIndex = MIN(MAX(index, 0), _logicalChildren.count);
|
|
2216
2730
|
[_logicalChildren insertObject:childComponentView atIndex:safeIndex];
|
|
2217
|
-
|
|
2731
|
+
if (_topScrollEdgeHard || _topOverlayView != nil) {
|
|
2732
|
+
_needsSlotRebuild = YES;
|
|
2733
|
+
} else {
|
|
2734
|
+
[self rebuildSlots];
|
|
2735
|
+
}
|
|
2218
2736
|
}
|
|
2219
2737
|
|
|
2220
2738
|
- (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView
|
|
@@ -2225,16 +2743,32 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2225
2743
|
if (existingIndex == NSNotFound) return;
|
|
2226
2744
|
[childComponentView removeFromSuperview];
|
|
2227
2745
|
[_logicalChildren removeObjectAtIndex:existingIndex];
|
|
2228
|
-
|
|
2746
|
+
if (_topScrollEdgeHard || _topOverlayView != nil) {
|
|
2747
|
+
_needsSlotRebuild = YES;
|
|
2748
|
+
} else {
|
|
2749
|
+
[self rebuildSlots];
|
|
2750
|
+
}
|
|
2229
2751
|
}
|
|
2230
2752
|
|
|
2231
2753
|
- (void)rebuildSlots
|
|
2232
2754
|
{
|
|
2755
|
+
if (_deferTopEdgeSlotRebuild) {
|
|
2756
|
+
_needsSlotRebuild = YES;
|
|
2757
|
+
return;
|
|
2758
|
+
}
|
|
2233
2759
|
if (_transitioning || _isPagerDragging) {
|
|
2234
2760
|
_needsSlotRebuild = YES;
|
|
2235
2761
|
return;
|
|
2236
2762
|
}
|
|
2763
|
+
if (_topScrollEdgeHard &&
|
|
2764
|
+
_logicalChildren.count != _pageKeys.count + 2 &&
|
|
2765
|
+
_logicalChildren.count != _pageKeys.count + 3) {
|
|
2766
|
+
_needsSlotRebuild = YES;
|
|
2767
|
+
return;
|
|
2768
|
+
}
|
|
2237
2769
|
_needsSlotRebuild = NO;
|
|
2770
|
+
_deferTopEdgeSlotRebuild = NO;
|
|
2771
|
+
[self restoreScrollEdgeContentMasks];
|
|
2238
2772
|
[self detachScrollObserver];
|
|
2239
2773
|
[self restoreSharedHeadersToContainer];
|
|
2240
2774
|
// OneKey patch: Fabric reuses these views without resetting transforms
|
|
@@ -2246,11 +2780,15 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2246
2780
|
_nativeSubHeaderView.transform = CGAffineTransformIdentity;
|
|
2247
2781
|
[_headerView removeFromSuperview];
|
|
2248
2782
|
[_stickyHeaderView removeFromSuperview];
|
|
2783
|
+
[self updateTopScrollEdgeForScrollView:nil];
|
|
2784
|
+
[_topOverlayView removeFromSuperview];
|
|
2249
2785
|
for (UIViewController *controller in _pageControllers) {
|
|
2250
2786
|
[controller.view removeFromSuperview];
|
|
2251
2787
|
}
|
|
2252
2788
|
_headerView = nil;
|
|
2253
2789
|
_stickyHeaderView = nil;
|
|
2790
|
+
_topOverlayView = nil;
|
|
2791
|
+
[self updateTopScrollEdgeForScrollView:nil];
|
|
2254
2792
|
[_pageControllers removeAllObjects];
|
|
2255
2793
|
|
|
2256
2794
|
if (_logicalChildren.count > 0) {
|
|
@@ -2261,7 +2799,15 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2261
2799
|
_stickyHeaderView = _logicalChildren[1];
|
|
2262
2800
|
[_sharedHeaderHostView addSubview:_stickyHeaderView];
|
|
2263
2801
|
}
|
|
2264
|
-
|
|
2802
|
+
BOOL hasOverlaySlot = _topScrollEdgeHard &&
|
|
2803
|
+
_logicalChildren.count == _pageKeys.count + 3;
|
|
2804
|
+
NSInteger pageSlotStart = hasOverlaySlot ? 3 : 2;
|
|
2805
|
+
if (hasOverlaySlot) {
|
|
2806
|
+
_topOverlayView = _logicalChildren[2];
|
|
2807
|
+
[_topOverlayHostView addSubview:_topOverlayView];
|
|
2808
|
+
}
|
|
2809
|
+
[self resolveExternalTopOverlay];
|
|
2810
|
+
for (NSInteger index = pageSlotStart; index < _logicalChildren.count; index++) {
|
|
2265
2811
|
UIView *page = _logicalChildren[index];
|
|
2266
2812
|
UIViewController *controller = [UIViewController new];
|
|
2267
2813
|
[controller.view addSubview:page];
|
|
@@ -2300,6 +2846,10 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2300
2846
|
if (!_nativeTabBarView.hidden) [_sharedHeaderHostView bringSubviewToFront:_nativeTabBarView];
|
|
2301
2847
|
if (!_nativeSubHeaderView.hidden) [_sharedHeaderHostView bringSubviewToFront:_nativeSubHeaderView];
|
|
2302
2848
|
[_containerView bringSubviewToFront:_sharedHeaderHostView];
|
|
2849
|
+
if (_stickyHeaderIsPinned) {
|
|
2850
|
+
[_pinnedStickyHostView.superview bringSubviewToFront:_pinnedStickyHostView];
|
|
2851
|
+
}
|
|
2852
|
+
if (self.hasFixedTopOverlay) [_containerView bringSubviewToFront:_topOverlayHostView];
|
|
2303
2853
|
[self setNeedsLayout];
|
|
2304
2854
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
2305
2855
|
if (!self->_isBeingRecycled) {
|
|
@@ -2312,7 +2862,10 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2312
2862
|
- (void)layoutSubviews
|
|
2313
2863
|
{
|
|
2314
2864
|
[super layoutSubviews];
|
|
2865
|
+
[self syncScrollEdgeAppearance];
|
|
2315
2866
|
_containerView.frame = self.bounds;
|
|
2867
|
+
[self registerExternalOuterPager];
|
|
2868
|
+
BOOL externalHeightChanged = [self resolveExternalTopOverlay];
|
|
2316
2869
|
if (_directNativePagerEnabled) {
|
|
2317
2870
|
_pagerScrollView.frame = _containerView.bounds;
|
|
2318
2871
|
} else {
|
|
@@ -2375,6 +2928,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2375
2928
|
}
|
|
2376
2929
|
}
|
|
2377
2930
|
[self layoutSharedHeaders];
|
|
2931
|
+
if (externalHeightChanged) [self reapplyInsetsToObservedScrollView];
|
|
2932
|
+
[self updateTopScrollEdgeForScrollView:_observedScrollView];
|
|
2933
|
+
[self updateScrollEdgeContentMasks];
|
|
2378
2934
|
[self updateHeaderSettledStateForceBusy:NO];
|
|
2379
2935
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
2380
2936
|
if (!self->_isBeingRecycled) {
|
|
@@ -2386,9 +2942,16 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2386
2942
|
|
|
2387
2943
|
- (void)prepareForRecycle
|
|
2388
2944
|
{
|
|
2945
|
+
[_externalOuterPager removeNestedOverlayObserver:self];
|
|
2946
|
+
_externalOuterPager = nil;
|
|
2947
|
+
[self cancelTopInsetReboundCheck];
|
|
2948
|
+
[self restoreScrollEdgeContentMasks];
|
|
2949
|
+
[self updateTopScrollEdgeForScrollView:nil];
|
|
2389
2950
|
[self cancelHeaderSettledCheck];
|
|
2390
2951
|
[self publishHeaderSettledAtTop:NO identityChanged:NO];
|
|
2391
2952
|
_isBeingRecycled = YES;
|
|
2953
|
+
_pendingTopOverlayAppearanceCommit = NO;
|
|
2954
|
+
[self syncScrollEdgeAppearance];
|
|
2392
2955
|
_generation++;
|
|
2393
2956
|
[_mountingRemovedScrollViews removeAllObjects];
|
|
2394
2957
|
_needsScrollObserverReattachAfterMount = NO;
|
|
@@ -2408,9 +2971,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2408
2971
|
if (appliedTop != nil && scrollView.refreshControl != nil) {
|
|
2409
2972
|
original.top += MAX(0, scrollView.contentInset.top - appliedTop.doubleValue);
|
|
2410
2973
|
}
|
|
2411
|
-
[self offsetDiag:@"release-original-inset-before" page:_currentIndex scroll:scrollView];
|
|
2412
2974
|
scrollView.contentInset = original;
|
|
2413
|
-
[self offsetDiag:@"release-original-inset-after" page:_currentIndex scroll:scrollView];
|
|
2414
2975
|
}
|
|
2415
2976
|
NSValue *indicatorValue = [_originalIndicatorInsets objectForKey:scrollView];
|
|
2416
2977
|
if (indicatorValue != nil) {
|
|
@@ -2439,6 +3000,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2439
3000
|
_nativeSubHeaderView.transform = CGAffineTransformIdentity;
|
|
2440
3001
|
[_headerView removeFromSuperview];
|
|
2441
3002
|
[_stickyHeaderView removeFromSuperview];
|
|
3003
|
+
[_topOverlayView removeFromSuperview];
|
|
2442
3004
|
for (UIViewController *controller in _pageControllers) {
|
|
2443
3005
|
[controller.view removeFromSuperview];
|
|
2444
3006
|
}
|
|
@@ -2446,6 +3008,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2446
3008
|
[_pageControllers removeAllObjects];
|
|
2447
3009
|
_headerView = nil;
|
|
2448
3010
|
_stickyHeaderView = nil;
|
|
3011
|
+
_topOverlayView = nil;
|
|
3012
|
+
_topOverlayHostView.hidden = YES;
|
|
3013
|
+
[self updateTopScrollEdgeForScrollView:nil];
|
|
2449
3014
|
_pageViewController.dataSource = nil;
|
|
2450
3015
|
_pageViewController.delegate = nil;
|
|
2451
3016
|
_pagerScrollView.delegate = nil;
|
|
@@ -2478,6 +3043,13 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2478
3043
|
_pendingInitialPage = 0;
|
|
2479
3044
|
_headerHeight = 0;
|
|
2480
3045
|
_stickyHeaderHeight = 0;
|
|
3046
|
+
_topOverlayHeight = 0;
|
|
3047
|
+
_externalTopOverlayTag = 0;
|
|
3048
|
+
_externalTopOverlayView = nil;
|
|
3049
|
+
_externalTopOverlayHeight = 0;
|
|
3050
|
+
_externalOuterPageActive = NO;
|
|
3051
|
+
_externalOuterTransitioning = NO;
|
|
3052
|
+
_topScrollEdgeHard = NO;
|
|
2481
3053
|
_nativeTabBarHeight = 44;
|
|
2482
3054
|
_nativeSubHeaderHeight = 74;
|
|
2483
3055
|
_headerOffset = 0;
|
|
@@ -2501,6 +3073,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2501
3073
|
_pendingGoToIndex = -1;
|
|
2502
3074
|
_pendingGoToAnimated = YES;
|
|
2503
3075
|
_needsSlotRebuild = NO;
|
|
3076
|
+
_deferTopEdgeSlotRebuild = NO;
|
|
2504
3077
|
_hasAppliedInitialPage = NO;
|
|
2505
3078
|
// OneKey patch: Fabric retains old props when recycling this view.
|
|
2506
3079
|
_needsPropsReapply = YES;
|
|
@@ -2517,6 +3090,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2517
3090
|
{
|
|
2518
3091
|
const auto &oldViewProps = *std::static_pointer_cast<const RNCCollapsiblePagerViewProps>(_props);
|
|
2519
3092
|
const auto &newViewProps = *std::static_pointer_cast<const RNCCollapsiblePagerViewProps>(props);
|
|
3093
|
+
_applyingTopOverlayInsetChange =
|
|
3094
|
+
oldViewProps.topScrollEdgeHard != newViewProps.topScrollEdgeHard ||
|
|
3095
|
+
oldViewProps.topOverlayHeight != newViewProps.topOverlayHeight;
|
|
2520
3096
|
|
|
2521
3097
|
if (_needsPropsReapply || oldViewProps.initialPage != newViewProps.initialPage) {
|
|
2522
3098
|
_pendingInitialPage = MAX(0, newViewProps.initialPage);
|
|
@@ -2566,6 +3142,41 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2566
3142
|
_stickyHeaderHeight = MAX(0, newViewProps.stickyHeaderHeight);
|
|
2567
3143
|
insetsChanged = YES;
|
|
2568
3144
|
}
|
|
3145
|
+
if (_needsPropsReapply || oldViewProps.topScrollEdgeHard != newViewProps.topScrollEdgeHard) {
|
|
3146
|
+
_topScrollEdgeHard = newViewProps.topScrollEdgeHard;
|
|
3147
|
+
_deferTopEdgeSlotRebuild = YES;
|
|
3148
|
+
[self rebuildSlots];
|
|
3149
|
+
NSUInteger generation = _generation;
|
|
3150
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
3151
|
+
if (self->_generation != generation || self->_isBeingRecycled ||
|
|
3152
|
+
!self->_deferTopEdgeSlotRebuild) return;
|
|
3153
|
+
self->_deferTopEdgeSlotRebuild = NO;
|
|
3154
|
+
[self rebuildSlots];
|
|
3155
|
+
});
|
|
3156
|
+
insetsChanged = YES;
|
|
3157
|
+
}
|
|
3158
|
+
if (_needsPropsReapply ||
|
|
3159
|
+
oldViewProps.topOverlayColorScheme != newViewProps.topOverlayColorScheme) {
|
|
3160
|
+
NSString *nextScheme = RCTNSStringFromString(newViewProps.topOverlayColorScheme);
|
|
3161
|
+
if (self.hasFixedTopOverlay && _topScrollEdgeHard &&
|
|
3162
|
+
_topOverlayColorScheme != nil &&
|
|
3163
|
+
![_topOverlayColorScheme isEqualToString:nextScheme]) {
|
|
3164
|
+
_pendingTopOverlayAppearanceCommit = YES;
|
|
3165
|
+
[self updateTopScrollEdgeForScrollView:nil];
|
|
3166
|
+
}
|
|
3167
|
+
_topOverlayColorScheme = nextScheme;
|
|
3168
|
+
[self syncScrollEdgeAppearance];
|
|
3169
|
+
}
|
|
3170
|
+
if (_needsPropsReapply || oldViewProps.topOverlayHeight != newViewProps.topOverlayHeight) {
|
|
3171
|
+
_topOverlayHeight = MAX(0, newViewProps.topOverlayHeight);
|
|
3172
|
+
insetsChanged = YES;
|
|
3173
|
+
}
|
|
3174
|
+
if (_needsPropsReapply ||
|
|
3175
|
+
oldViewProps.externalTopOverlayTag != newViewProps.externalTopOverlayTag) {
|
|
3176
|
+
_externalTopOverlayTag = newViewProps.externalTopOverlayTag;
|
|
3177
|
+
[self registerExternalOuterPager];
|
|
3178
|
+
insetsChanged = [self resolveExternalTopOverlay] || insetsChanged;
|
|
3179
|
+
}
|
|
2569
3180
|
if (_needsPropsReapply || oldViewProps.pageKeys != newViewProps.pageKeys) {
|
|
2570
3181
|
NSString *json = RCTNSStringFromString(newViewProps.pageKeys);
|
|
2571
3182
|
NSData *data = [json dataUsingEncoding:NSUTF8StringEncoding];
|
|
@@ -2633,17 +3244,23 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2633
3244
|
|
|
2634
3245
|
_needsPropsReapply = NO;
|
|
2635
3246
|
[super updateProps:props oldProps:oldProps];
|
|
3247
|
+
[self updateTopScrollEdgeForScrollView:_observedScrollView];
|
|
2636
3248
|
if (insetsChanged) {
|
|
2637
3249
|
[self reapplyInsetsToObservedScrollView];
|
|
2638
3250
|
[self setNeedsLayout];
|
|
2639
3251
|
}
|
|
3252
|
+
_applyingTopOverlayInsetChange = NO;
|
|
2640
3253
|
if (!_nativeTabBarView.hidden) {
|
|
2641
|
-
[
|
|
3254
|
+
[_nativeTabBarView.superview bringSubviewToFront:_nativeTabBarView];
|
|
2642
3255
|
}
|
|
2643
3256
|
if (!_nativeSubHeaderView.hidden) {
|
|
2644
|
-
[
|
|
3257
|
+
[_nativeSubHeaderView.superview bringSubviewToFront:_nativeSubHeaderView];
|
|
2645
3258
|
}
|
|
2646
3259
|
[_sharedHeaderHostView.superview bringSubviewToFront:_sharedHeaderHostView];
|
|
3260
|
+
if (_stickyHeaderIsPinned) {
|
|
3261
|
+
[_pinnedStickyHostView.superview bringSubviewToFront:_pinnedStickyHostView];
|
|
3262
|
+
}
|
|
3263
|
+
if (self.hasFixedTopOverlay) [_containerView bringSubviewToFront:_topOverlayHostView];
|
|
2647
3264
|
}
|
|
2648
3265
|
|
|
2649
3266
|
#pragma mark - Collapsible scroll coordination
|
|
@@ -2877,21 +3494,17 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2877
3494
|
return;
|
|
2878
3495
|
}
|
|
2879
3496
|
|
|
2880
|
-
[self offsetDiag:@"observer-candidate" page:_currentIndex scroll:candidate];
|
|
2881
3497
|
BOOL replacingCurrentScrollView = _observingContentOffset &&
|
|
2882
3498
|
(!_nativeSmoothHeaderScrollEnabled || [_observedPageKey isEqualToString:self.currentPageKey]);
|
|
2883
3499
|
[self detachScrollObserver];
|
|
2884
3500
|
if (replacingCurrentScrollView) {
|
|
2885
3501
|
// Legacy replacement discards body state; smooth replacement waits for layout.
|
|
2886
|
-
[self offsetDiag:@"observer-replacement-cache-before" page:_currentIndex scroll:candidate];
|
|
2887
3502
|
if (!_nativeSmoothHeaderScrollEnabled) _pageOffsets[self.currentPageKey] = @(_headerOffset);
|
|
2888
|
-
[self offsetDiag:@"observer-replacement-cache-after" page:_currentIndex scroll:candidate];
|
|
2889
3503
|
}
|
|
2890
3504
|
_observedScrollView = candidate;
|
|
2891
3505
|
_observedPageKey = [self.currentPageKey copy];
|
|
2892
3506
|
_observedPageIndex = _currentIndex;
|
|
2893
3507
|
_observedPageGeneration = _generation;
|
|
2894
|
-
[self offsetDiag:@"observer-attached-before-restore" page:_currentIndex scroll:candidate];
|
|
2895
3508
|
[self applyInsetsToScrollView:candidate pageIndex:_currentIndex restore:YES];
|
|
2896
3509
|
[candidate addObserver:self
|
|
2897
3510
|
forKeyPath:@"contentOffset"
|
|
@@ -2909,6 +3522,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2909
3522
|
_observingContentOffset = YES;
|
|
2910
3523
|
_needsScrollObserverReattachAfterMount = NO;
|
|
2911
3524
|
_currentLogicalOffset = candidate.contentOffset.y + candidate.contentInset.top;
|
|
3525
|
+
[self updateTopScrollEdgeForScrollView:candidate];
|
|
2912
3526
|
[self attachSharedHeadersToScrollView:candidate];
|
|
2913
3527
|
[self updateSharedHeaderPressCancellationGesture];
|
|
2914
3528
|
[self updateHeaderSettledStateForceBusy:NO];
|
|
@@ -2916,6 +3530,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2916
3530
|
|
|
2917
3531
|
- (void)detachScrollObserver
|
|
2918
3532
|
{
|
|
3533
|
+
[self cancelTopInsetReboundCheck];
|
|
3534
|
+
[self restorePinnedStickyHeader];
|
|
3535
|
+
[self updateTopScrollEdgeForScrollView:nil];
|
|
2919
3536
|
[self cancelHeaderSettledCheck];
|
|
2920
3537
|
_headerCommandOwner = nil;
|
|
2921
3538
|
_headerSettledGeometry = nil;
|
|
@@ -2925,7 +3542,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2925
3542
|
UIScrollView *scrollView = _observedScrollView;
|
|
2926
3543
|
if (scrollView != nil) {
|
|
2927
3544
|
[scrollView.panGestureRecognizer removeTarget:self action:@selector(observedListPanChanged:)];
|
|
2928
|
-
[self offsetDiag:@"detach-save-before" page:_currentIndex scroll:scrollView];
|
|
2929
3545
|
NSString *ownerKey = _nativeSmoothHeaderScrollEnabled ? _observedPageKey : self.currentPageKey;
|
|
2930
3546
|
BOOL ownerKeyExists = ownerKey != nil && ([_pageKeys containsObject:ownerKey] ||
|
|
2931
3547
|
(_observedPageIndex >= 0 && _observedPageIndex < _pageControllers.count &&
|
|
@@ -2933,7 +3549,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2933
3549
|
if (ownerKey != nil && !_nativeSmoothHeaderScrollEnabled) {
|
|
2934
3550
|
_pageOffsets[ownerKey] = @(scrollView.contentOffset.y + scrollView.contentInset.top);
|
|
2935
3551
|
}
|
|
2936
|
-
[self offsetDiag:@"detach-save-after" page:_currentIndex scroll:scrollView];
|
|
2937
3552
|
if (_observingContentOffset) {
|
|
2938
3553
|
[scrollView removeObserver:self
|
|
2939
3554
|
forKeyPath:@"contentOffset"
|
|
@@ -2975,6 +3590,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2975
3590
|
removeGestureRecognizer:_sharedHeaderOuterPagerGesture];
|
|
2976
3591
|
}
|
|
2977
3592
|
_sharedHeaderOuterPagerGesture.blockedPagerGestures = nil;
|
|
3593
|
+
_sharedHeaderOuterPagerGesture.trackedTouch = nil;
|
|
3594
|
+
_sharedHeaderOuterPagerGesture.protectsNestedBody = NO;
|
|
3595
|
+
_sharedHeaderOuterPagerGesture.cancelsTouchesInView = YES;
|
|
2978
3596
|
_sharedHeaderPressCancellationGestureHost = nil;
|
|
2979
3597
|
_reactTouchHandler = nil;
|
|
2980
3598
|
}
|
|
@@ -3024,7 +3642,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3024
3642
|
}
|
|
3025
3643
|
}
|
|
3026
3644
|
if (_sharedHeaderPressCancellationGesture.view == gestureHost &&
|
|
3027
|
-
_sharedHeaderOuterPagerGesture.view ==
|
|
3645
|
+
_sharedHeaderOuterPagerGesture.view == _containerView &&
|
|
3028
3646
|
_reactTouchHandler == reactTouchHandler) {
|
|
3029
3647
|
_sharedHeaderOuterPagerGesture.blockedPagerGestures = outerPagerGestures;
|
|
3030
3648
|
return;
|
|
@@ -3066,7 +3684,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3066
3684
|
_reactTouchHandler = reactTouchHandler;
|
|
3067
3685
|
_sharedHeaderPressCancellationGestureHost = gestureHost;
|
|
3068
3686
|
[gestureHost addGestureRecognizer:_sharedHeaderPressCancellationGesture];
|
|
3069
|
-
[
|
|
3687
|
+
[_containerView addGestureRecognizer:_sharedHeaderOuterPagerGesture];
|
|
3070
3688
|
RNCCollapsiblePagerLog([NSString stringWithFormat:
|
|
3071
3689
|
@"press-cancel-bridge attached=1 outer-pagers=%lu",
|
|
3072
3690
|
(unsigned long)outerPagerGestures.count]);
|
|
@@ -3138,6 +3756,11 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3138
3756
|
|
|
3139
3757
|
- (void)observedListPanChanged:(UIPanGestureRecognizer *)recognizer
|
|
3140
3758
|
{
|
|
3759
|
+
if (recognizer == _pendingTopInsetReboundScrollView.panGestureRecognizer &&
|
|
3760
|
+
(recognizer.state == UIGestureRecognizerStateBegan ||
|
|
3761
|
+
recognizer.state == UIGestureRecognizerStateChanged)) {
|
|
3762
|
+
[self cancelTopInsetReboundCheck];
|
|
3763
|
+
}
|
|
3141
3764
|
if (recognizer == _observedScrollView.panGestureRecognizer) {
|
|
3142
3765
|
[self updateHeaderSettledStateForceBusy:
|
|
3143
3766
|
recognizer.state == UIGestureRecognizerStateBegan || recognizer.state == UIGestureRecognizerStateChanged];
|
|
@@ -3205,7 +3828,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3205
3828
|
[_managedRefreshControls removeObjectForKey:scrollView];
|
|
3206
3829
|
if (current != nil) [_managedRefreshControls setObject:current forKey:scrollView];
|
|
3207
3830
|
}
|
|
3208
|
-
[RNCRefreshControlOffsetState setOffset:_headerHeight + _stickyHeaderHeight
|
|
3831
|
+
[RNCRefreshControlOffsetState setOffset:self.effectiveTopOverlayHeight + _headerHeight + _stickyHeaderHeight
|
|
3209
3832
|
forControl:current owner:self];
|
|
3210
3833
|
}
|
|
3211
3834
|
|
|
@@ -3219,6 +3842,53 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3219
3842
|
[_managedRefreshControls removeObjectForKey:scrollView];
|
|
3220
3843
|
}
|
|
3221
3844
|
|
|
3845
|
+
- (void)cancelTopInsetReboundCheck
|
|
3846
|
+
{
|
|
3847
|
+
[_topInsetReboundTimer invalidate];
|
|
3848
|
+
_topInsetReboundTimer = nil;
|
|
3849
|
+
_pendingTopInsetReboundScrollView = nil;
|
|
3850
|
+
_pendingTopInsetReboundPageKey = nil;
|
|
3851
|
+
}
|
|
3852
|
+
|
|
3853
|
+
- (void)scheduleTopInsetReboundCheck
|
|
3854
|
+
{
|
|
3855
|
+
if (_pendingTopInsetReboundScrollView == nil ||
|
|
3856
|
+
_pendingTopInsetReboundScrollView != _observedScrollView) return;
|
|
3857
|
+
[_topInsetReboundTimer invalidate];
|
|
3858
|
+
__weak __typeof__(self) weakSelf = self;
|
|
3859
|
+
_topInsetReboundTimer = [NSTimer scheduledTimerWithTimeInterval:0.15 repeats:NO block:^(NSTimer *timer) {
|
|
3860
|
+
__strong __typeof__(weakSelf) self = weakSelf;
|
|
3861
|
+
if (self == nil || self->_topInsetReboundTimer != timer) return;
|
|
3862
|
+
self->_topInsetReboundTimer = nil;
|
|
3863
|
+
UIScrollView *scrollView = self->_pendingTopInsetReboundScrollView;
|
|
3864
|
+
if (scrollView == nil || scrollView != self->_observedScrollView ||
|
|
3865
|
+
self->_pendingTopInsetReboundGeneration != self->_generation ||
|
|
3866
|
+
![self->_pendingTopInsetReboundPageKey isEqualToString:self.currentPageKey] ||
|
|
3867
|
+
self->_isBeingRecycled || self.window == nil ||
|
|
3868
|
+
self->_transitioning || self->_isPagerDragging ||
|
|
3869
|
+
self->_headerCommandOwner == scrollView || self->_pagerTopPageKey != nil) {
|
|
3870
|
+
[self cancelTopInsetReboundCheck];
|
|
3871
|
+
return;
|
|
3872
|
+
}
|
|
3873
|
+
UIGestureRecognizerState pan = scrollView.panGestureRecognizer.state;
|
|
3874
|
+
if (scrollView.refreshControl.isRefreshing || scrollView.isDragging ||
|
|
3875
|
+
scrollView.isDecelerating || scrollView.isTracking ||
|
|
3876
|
+
pan == UIGestureRecognizerStateBegan || pan == UIGestureRecognizerStateChanged ||
|
|
3877
|
+
self->_syntheticPageRestore || self->_transitionPageOffsets != nil) {
|
|
3878
|
+
[self scheduleTopInsetReboundCheck];
|
|
3879
|
+
return;
|
|
3880
|
+
}
|
|
3881
|
+
CGFloat minimumOffset = -scrollView.adjustedContentInset.top;
|
|
3882
|
+
[self cancelTopInsetReboundCheck];
|
|
3883
|
+
if (scrollView.contentOffset.y < minimumOffset - 0.5) {
|
|
3884
|
+
// UIKit can finish a refresh rebound at the inset that preceded an overlay resize.
|
|
3885
|
+
self->_correctingTopInsetRebound = YES;
|
|
3886
|
+
[scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x, minimumOffset) animated:NO];
|
|
3887
|
+
self->_correctingTopInsetRebound = NO;
|
|
3888
|
+
}
|
|
3889
|
+
}];
|
|
3890
|
+
}
|
|
3891
|
+
|
|
3222
3892
|
// OneKey patch: release observers, headers and saved state while the scroll view
|
|
3223
3893
|
// still represents the old list, before Fabric applies its next owner's props.
|
|
3224
3894
|
- (void)restoreScrollViewState:(UIScrollView *)scrollView resetSavedOffset:(BOOL)resetSavedOffset
|
|
@@ -3227,9 +3897,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3227
3897
|
[self releaseRefreshControlForScrollView:scrollView];
|
|
3228
3898
|
if (scrollView == _observedScrollView) {
|
|
3229
3899
|
[self detachScrollObserver];
|
|
3230
|
-
[self offsetDiag:resetSavedOffset ? @"release-reset-before" : @"release-retain-before" page:_currentIndex scroll:scrollView];
|
|
3231
3900
|
if (resetSavedOffset && !_nativeSmoothHeaderScrollEnabled) _pageOffsets[self.currentPageKey] = @(_headerOffset);
|
|
3232
|
-
[self offsetDiag:@"release-reset-after" page:_currentIndex scroll:scrollView];
|
|
3233
3901
|
}
|
|
3234
3902
|
if (scrollView == _sharedHeaderScrollView) {
|
|
3235
3903
|
[self restoreSharedHeadersToContainer];
|
|
@@ -3249,10 +3917,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3249
3917
|
[_originalDirectionalLockEnabled objectForKey:scrollView].boolValue;
|
|
3250
3918
|
scrollView.contentInsetAdjustmentBehavior = (UIScrollViewContentInsetAdjustmentBehavior)
|
|
3251
3919
|
[_originalInsetAdjustmentBehavior objectForKey:scrollView].integerValue;
|
|
3252
|
-
[self offsetDiag:@"release-original-offset-before" page:_currentIndex scroll:scrollView];
|
|
3253
3920
|
[scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x, logicalOffset - original.top)
|
|
3254
3921
|
animated:NO];
|
|
3255
|
-
[self offsetDiag:@"release-original-offset-after" page:_currentIndex scroll:scrollView];
|
|
3256
3922
|
[_originalInsets removeObjectForKey:scrollView];
|
|
3257
3923
|
[_appliedTopInsets removeObjectForKey:scrollView];
|
|
3258
3924
|
[_originalIndicatorInsets removeObjectForKey:scrollView];
|
|
@@ -3283,7 +3949,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3283
3949
|
{
|
|
3284
3950
|
// Smooth pages retain their complete offset while replacement identity and
|
|
3285
3951
|
// layout settle. Legacy pages retain the historical header-only policy.
|
|
3286
|
-
[self offsetDiag:restore ? @"restore-enter" : @"insets-enter" page:pageIndex scroll:scrollView];
|
|
3287
3952
|
NSString *pageKey = [self pageKeyForIndex:pageIndex];
|
|
3288
3953
|
RNCCollapsiblePagerReleasedScrollState *released = _releasedPageScrollStates[pageKey];
|
|
3289
3954
|
BOOL deferredReleasedIdentity = NO;
|
|
@@ -3329,13 +3994,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3329
3994
|
deferredReleasedIdentity = now - released.pendingFallbackDetectedAt <
|
|
3330
3995
|
RNCCollapsiblePagerFallbackDebounceInterval;
|
|
3331
3996
|
}
|
|
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
3997
|
if (!deferredReleasedIdentity) {
|
|
3336
|
-
[self offsetDiag:@"released-identity-cache-before" page:pageIndex scroll:scrollView];
|
|
3337
3998
|
if (!sameList && !_nativeSmoothHeaderScrollEnabled) _pageOffsets[pageKey] = @(_headerOffset);
|
|
3338
|
-
[self offsetDiag:@"released-identity-cache-after" page:pageIndex scroll:scrollView];
|
|
3339
3999
|
[_releasedPageScrollStates removeObjectForKey:pageKey];
|
|
3340
4000
|
}
|
|
3341
4001
|
}
|
|
@@ -3360,22 +4020,24 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3360
4020
|
// suppress its later vertical movement while the pager is still pending.
|
|
3361
4021
|
scrollView.directionalLockEnabled = _nativeSmoothHeaderScrollEnabled
|
|
3362
4022
|
? NO : [_originalDirectionalLockEnabled objectForKey:scrollView].boolValue;
|
|
3363
|
-
[self updateNativeScrollerViewport:scrollView stickyHeight:_stickyHeaderHeight];
|
|
4023
|
+
[self updateNativeScrollerViewport:scrollView stickyHeight:_stickyHeaderHeight + self.effectiveTopOverlayHeight];
|
|
3364
4024
|
[self updateRefreshControlForScrollView:scrollView];
|
|
3365
4025
|
CGFloat previousTopInset = scrollView.contentInset.top;
|
|
3366
4026
|
CGFloat previousLogicalOffset = scrollView.contentOffset.y + previousTopInset;
|
|
4027
|
+
// Preserve UIKit's rebound after refresh while the content is beyond its top inset.
|
|
4028
|
+
BOOL wasReturningFromPull = scrollView.contentOffset.y < -scrollView.adjustedContentInset.top - 0.5;
|
|
3367
4029
|
NSNumber *appliedTop = [_appliedTopInsets objectForKey:scrollView];
|
|
3368
4030
|
// UIRefreshControl owns its temporary top inset, including while ending.
|
|
3369
4031
|
CGFloat refreshInset = appliedTop != nil && scrollView.refreshControl != nil
|
|
3370
4032
|
? MAX(0, previousTopInset - appliedTop.doubleValue) : 0;
|
|
3371
4033
|
UIEdgeInsets next = original;
|
|
3372
|
-
next.top += _headerHeight + _stickyHeaderHeight;
|
|
4034
|
+
next.top += self.effectiveTopOverlayHeight + _headerHeight + _stickyHeaderHeight;
|
|
3373
4035
|
CGFloat pagerTopInset = next.top;
|
|
3374
4036
|
next.top += refreshInset;
|
|
3375
4037
|
[_appliedTopInsets setObject:@(pagerTopInset) forKey:scrollView];
|
|
3376
4038
|
// Short pages still need enough range to collapse the header; long pages
|
|
3377
4039
|
// must end at the last row rather than leave a header-sized blank footer.
|
|
3378
|
-
next.bottom += MAX(0, scrollView.bounds.size.height - _stickyHeaderHeight
|
|
4040
|
+
next.bottom += MAX(0, scrollView.bounds.size.height - _stickyHeaderHeight - self.effectiveTopOverlayHeight
|
|
3379
4041
|
- original.top - original.bottom - scrollView.contentSize.height);
|
|
3380
4042
|
if (UIEdgeInsetsEqualToEdgeInsets(scrollView.contentInset, next) && !restore) {
|
|
3381
4043
|
// UICollectionView may report contentSize during a bounce without changing
|
|
@@ -3385,9 +4047,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3385
4047
|
}
|
|
3386
4048
|
BOOL previousSyntheticRestore = _syntheticPageRestore;
|
|
3387
4049
|
if (_nativeSmoothHeaderScrollEnabled || [_alignedBodyKeys containsObject:pageKey]) _syntheticPageRestore = YES;
|
|
3388
|
-
[self offsetDiag:@"inset-assignment-before" page:pageIndex scroll:scrollView];
|
|
3389
4050
|
scrollView.contentInset = next;
|
|
3390
|
-
[self offsetDiag:@"inset-assignment-after" page:pageIndex scroll:scrollView];
|
|
3391
4051
|
UIEdgeInsets indicatorInsets = scrollView.verticalScrollIndicatorInsets;
|
|
3392
4052
|
indicatorInsets.top = pagerTopInset;
|
|
3393
4053
|
scrollView.verticalScrollIndicatorInsets = indicatorInsets;
|
|
@@ -3419,9 +4079,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3419
4079
|
} else {
|
|
3420
4080
|
restoredLogicalOffset = deferredReleasedIdentity || saved == nil ? _headerOffset : saved.doubleValue;
|
|
3421
4081
|
}
|
|
3422
|
-
#if DEBUG
|
|
3423
|
-
[self offsetDiag:[NSString stringWithFormat:@"restore-candidate-saved=%.4f-headerAlignedLogical=%.4f", saved.doubleValue, restoredLogicalOffset] page:pageIndex scroll:scrollView];
|
|
3424
|
-
#endif
|
|
3425
4082
|
intendedLogicalOffset = restoredLogicalOffset;
|
|
3426
4083
|
// Cancellation restores the visible source, while retaining its unreachable reading intent.
|
|
3427
4084
|
if (_nativeSmoothHeaderScrollEnabled && _headerOffset >= _headerHeight &&
|
|
@@ -3434,9 +4091,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3434
4091
|
}
|
|
3435
4092
|
// An inactive list may shrink after its offset observer was detached.
|
|
3436
4093
|
// 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
4094
|
targetOffset = RNCLimitRestoredScrollOffset(
|
|
3441
4095
|
targetOffset,
|
|
3442
4096
|
scrollView.contentSize.height,
|
|
@@ -3445,13 +4099,17 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3445
4099
|
next.bottom
|
|
3446
4100
|
);
|
|
3447
4101
|
}
|
|
3448
|
-
#if DEBUG
|
|
3449
|
-
[self offsetDiag:[NSString stringWithFormat:@"restore-final-target=%.4f-restore=%d", targetOffset, restore] page:pageIndex scroll:scrollView];
|
|
3450
|
-
#endif
|
|
3451
4102
|
// A bottom-inset change must not cancel UIKit's refresh or bounce settlement.
|
|
3452
|
-
if (restore || previousTopInset != next.top) {
|
|
4103
|
+
if (restore || (previousTopInset != next.top && !wasReturningFromPull)) {
|
|
3453
4104
|
[scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x, targetOffset) animated:NO];
|
|
3454
4105
|
}
|
|
4106
|
+
if (_applyingTopOverlayInsetChange && !restore && wasReturningFromPull &&
|
|
4107
|
+
previousTopInset != next.top && scrollView == _observedScrollView) {
|
|
4108
|
+
_pendingTopInsetReboundScrollView = scrollView;
|
|
4109
|
+
_pendingTopInsetReboundPageKey = [pageKey copy];
|
|
4110
|
+
_pendingTopInsetReboundGeneration = _generation;
|
|
4111
|
+
[self scheduleTopInsetReboundCheck];
|
|
4112
|
+
}
|
|
3455
4113
|
if (_nativeSmoothHeaderScrollEnabled && restore && targetOffset + next.top + 0.5 < intendedLogicalOffset) {
|
|
3456
4114
|
[_pendingPageRestoreKeys addObject:pageKey];
|
|
3457
4115
|
}
|
|
@@ -3461,7 +4119,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3461
4119
|
if (!_nativeSmoothHeaderScrollEnabled) [self updateHeaderForScrollView:scrollView];
|
|
3462
4120
|
else [self applyHeaderOffset];
|
|
3463
4121
|
}
|
|
3464
|
-
[self offsetDiag:@"restore-post" page:pageIndex scroll:scrollView];
|
|
3465
4122
|
}
|
|
3466
4123
|
|
|
3467
4124
|
- (void)updateNativeScrollerViewport:(UIScrollView *)scrollView stickyHeight:(CGFloat)height
|
|
@@ -3478,6 +4135,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3478
4135
|
_observedPageKey == nil || ![_observedPageKey isEqualToString:self.currentPageKey] ||
|
|
3479
4136
|
_observedPageIndex != _currentIndex || _observedPageGeneration != _generation ||
|
|
3480
4137
|
_transitioning || _isPagerDragging || _transitionPageOffsets != nil || _syntheticPageRestore) return;
|
|
4138
|
+
[self cancelTopInsetReboundCheck];
|
|
3481
4139
|
NSInteger phase = [notification.userInfo[@"phase"] integerValue];
|
|
3482
4140
|
[self updateHeaderSettledStateForceBusy:NO];
|
|
3483
4141
|
if (phase == 1) _headerCommandOwner = scrollView;
|
|
@@ -3510,7 +4168,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3510
4168
|
_headerOffset = 0;
|
|
3511
4169
|
[self applyHeaderOffset];
|
|
3512
4170
|
}
|
|
3513
|
-
[self offsetDiag:@"list-command-body-takeover" page:_currentIndex scroll:scrollView];
|
|
3514
4171
|
}
|
|
3515
4172
|
|
|
3516
4173
|
- (void)nativeScrollerWillReceiveScrollCommand:(RNCNativeScrollerComponentView *)scroller
|
|
@@ -3522,12 +4179,12 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3522
4179
|
_observedPageIndex != _currentIndex || _currentIndex < 0 || _currentIndex >= _pageControllers.count ||
|
|
3523
4180
|
_observedPageGeneration != _generation || _transitioning || _isPagerDragging ||
|
|
3524
4181
|
_transitionPageOffsets != nil || _syntheticPageRestore) return;
|
|
4182
|
+
[self cancelTopInsetReboundCheck];
|
|
3525
4183
|
if (!_dispatchingPagerTop) {
|
|
3526
4184
|
[self cancelPagerTop];
|
|
3527
4185
|
[_alignedBodyKeys removeObject:ownerKey];
|
|
3528
4186
|
}
|
|
3529
4187
|
if (!_nativeSmoothHeaderScrollEnabled) return;
|
|
3530
|
-
[self offsetDiag:@"command-pending-cancel-before" page:_observedPageIndex scroll:scrollView];
|
|
3531
4188
|
[_pendingPageRestoreKeys removeObject:ownerKey];
|
|
3532
4189
|
_currentLogicalOffset = scrollView.contentOffset.y + scrollView.contentInset.top;
|
|
3533
4190
|
[_commandBodyKeys removeObject:ownerKey];
|
|
@@ -3536,7 +4193,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3536
4193
|
[_displayBodyOverrides removeObjectForKey:ownerKey];
|
|
3537
4194
|
_pageOffsets[ownerKey] = @(MAX(_currentLogicalOffset - _headerHeight, 0));
|
|
3538
4195
|
[self updateHeaderForScrollView:scrollView];
|
|
3539
|
-
[self offsetDiag:@"command-pending-cancel-after" page:_observedPageIndex scroll:scrollView];
|
|
3540
4196
|
}
|
|
3541
4197
|
|
|
3542
4198
|
|
|
@@ -3598,6 +4254,14 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3598
4254
|
return;
|
|
3599
4255
|
}
|
|
3600
4256
|
if (context == RNCCollapsiblePagerContentOffsetContext && object == _observedScrollView) {
|
|
4257
|
+
if (_pendingTopInsetReboundScrollView == object &&
|
|
4258
|
+
([keyPath isEqualToString:@"contentOffset"] || [keyPath isEqualToString:@"contentInset"])) {
|
|
4259
|
+
if (((UIScrollView *)object).panGestureRecognizer.state == UIGestureRecognizerStateChanged) {
|
|
4260
|
+
[self cancelTopInsetReboundCheck];
|
|
4261
|
+
} else {
|
|
4262
|
+
[self scheduleTopInsetReboundCheck];
|
|
4263
|
+
}
|
|
4264
|
+
}
|
|
3601
4265
|
if (![change[NSKeyValueChangeOldKey] isEqual:change[NSKeyValueChangeNewKey]]) {
|
|
3602
4266
|
[self updateHeaderSettledStateForceBusy:YES];
|
|
3603
4267
|
}
|
|
@@ -3662,18 +4326,12 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3662
4326
|
}
|
|
3663
4327
|
UIScrollView *scrollView = (UIScrollView *)object;
|
|
3664
4328
|
_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
|
|
4329
|
+
if (_correctingTopInsetRebound) return;
|
|
3669
4330
|
if ([_alignedBodyKeys containsObject:_observedPageKey] ||
|
|
3670
4331
|
(_nativeSmoothHeaderScrollEnabled && scrollView.panGestureRecognizer.state != UIGestureRecognizerStateChanged &&
|
|
3671
4332
|
!scrollView.isDecelerating && ![_commandBodyKeys containsObject:_observedPageKey])) return;
|
|
3672
4333
|
_pageOffsets[_nativeSmoothHeaderScrollEnabled ? _observedPageKey : self.currentPageKey] =
|
|
3673
4334
|
@(_nativeSmoothHeaderScrollEnabled ? MAX(_currentLogicalOffset - _headerHeight, 0) : _currentLogicalOffset);
|
|
3674
|
-
#if DEBUG
|
|
3675
|
-
if (offsetDiagChanged) [self offsetDiag:@"kvo-cache-after" page:_currentIndex scroll:scrollView];
|
|
3676
|
-
#endif
|
|
3677
4335
|
[self updateHeaderForScrollView:scrollView];
|
|
3678
4336
|
return;
|
|
3679
4337
|
}
|
|
@@ -3714,17 +4372,22 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3714
4372
|
? MAX(0, scrollView.contentInset.top - appliedTop.doubleValue) : 0;
|
|
3715
4373
|
CGFloat pinnedTranslation = MAX(0, _currentLogicalOffset - _headerHeight - refreshInset);
|
|
3716
4374
|
CGAffineTransform stickyTransform = CGAffineTransformMakeTranslation(0, pinnedTranslation);
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
4375
|
+
[self updatePinnedStickyHeader];
|
|
4376
|
+
_stickyHeaderView.transform = _stickyHeaderIsPinned ? CGAffineTransformIdentity : stickyTransform;
|
|
4377
|
+
_nativeTabBarView.transform = _stickyHeaderIsPinned ? CGAffineTransformIdentity : stickyTransform;
|
|
4378
|
+
_nativeSubHeaderView.transform = _stickyHeaderIsPinned ? CGAffineTransformIdentity : stickyTransform;
|
|
4379
|
+
[self updateScrollEdgeContentMasks];
|
|
3720
4380
|
return;
|
|
3721
4381
|
}
|
|
4382
|
+
[self updatePinnedStickyHeader];
|
|
3722
4383
|
CGAffineTransform collapseTransform = CGAffineTransformMakeTranslation(0, -_headerOffset);
|
|
3723
4384
|
_sharedHeaderHostView.transform = collapseTransform;
|
|
3724
4385
|
_headerView.transform = CGAffineTransformIdentity;
|
|
3725
4386
|
_stickyHeaderView.transform = CGAffineTransformIdentity;
|
|
3726
4387
|
_nativeTabBarView.transform = CGAffineTransformIdentity;
|
|
3727
4388
|
_nativeSubHeaderView.transform = CGAffineTransformIdentity;
|
|
4389
|
+
[self updateTopScrollEdgeForScrollView:_observedScrollView];
|
|
4390
|
+
[self updateScrollEdgeContentMasks];
|
|
3728
4391
|
}
|
|
3729
4392
|
|
|
3730
4393
|
#pragma mark - Pager navigation
|
|
@@ -3736,7 +4399,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3736
4399
|
|
|
3737
4400
|
- (void)goTo:(NSInteger)index animated:(BOOL)animated
|
|
3738
4401
|
{
|
|
3739
|
-
[self
|
|
4402
|
+
[self cancelTopInsetReboundCheck];
|
|
3740
4403
|
if (_isBeingRecycled || index < 0 || index >= _pageControllers.count) return;
|
|
3741
4404
|
_hasReceivedPageCommand = YES;
|
|
3742
4405
|
_hasAppliedInitialPage = YES;
|
|
@@ -3867,6 +4530,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3867
4530
|
if (!self->_transitioning && !self->_needsSlotRebuild) {
|
|
3868
4531
|
[self attachScrollObserverForCurrentPage];
|
|
3869
4532
|
}
|
|
4533
|
+
[self updateScrollEdgeContentMasks];
|
|
3870
4534
|
});
|
|
3871
4535
|
}
|
|
3872
4536
|
|
|
@@ -3918,7 +4582,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3918
4582
|
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
|
|
3919
4583
|
{
|
|
3920
4584
|
[self updateHeaderSettledStateForceBusy:YES];
|
|
3921
|
-
[self offsetDiag:@"pager-drag-begin" page:_currentIndex scroll:_observedScrollView];
|
|
3922
4585
|
if (_nativeSmoothHeaderScrollEnabled && scrollView == _pagerScrollView) {
|
|
3923
4586
|
[_contentPressCancellationGesture cancelPressForPan:scrollView.panGestureRecognizer];
|
|
3924
4587
|
}
|
|
@@ -4075,6 +4738,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4075
4738
|
CGFloat offset = progress - position;
|
|
4076
4739
|
position = MAX(0, MIN(position, (NSInteger)_pageControllers.count - 1));
|
|
4077
4740
|
[self updatePageStateTransitionProgress:progress];
|
|
4741
|
+
[self updateScrollEdgeContentMasks];
|
|
4078
4742
|
[_nativeTabBarView setProgress:progress];
|
|
4079
4743
|
const auto emitter = [self eventEmitter];
|
|
4080
4744
|
if (emitter) {
|
|
@@ -4093,6 +4757,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4093
4757
|
tabProgress = _currentIndex + (_destinationIndex - _currentIndex) * transitionProgress;
|
|
4094
4758
|
}
|
|
4095
4759
|
[self updatePageStateTransitionProgress:tabProgress];
|
|
4760
|
+
[self updateScrollEdgeContentMasks];
|
|
4096
4761
|
[_nativeTabBarView setProgress:tabProgress];
|
|
4097
4762
|
const auto emitter = [self eventEmitter];
|
|
4098
4763
|
if (emitter) {
|
|
@@ -4110,7 +4775,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4110
4775
|
|
|
4111
4776
|
- (void)emitPageSelected:(NSInteger)position
|
|
4112
4777
|
{
|
|
4113
|
-
[self offsetDiag:@"selection-complete" page:position scroll:_observedScrollView];
|
|
4114
4778
|
const auto emitter = [self eventEmitter];
|
|
4115
4779
|
if (emitter) emitter->onPageSelected({.position = (double)position});
|
|
4116
4780
|
}
|
|
@@ -4255,12 +4919,12 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4255
4919
|
_currentLogicalOffset = logical;
|
|
4256
4920
|
_headerOffset = _pagerTopLogicalTarget;
|
|
4257
4921
|
[self applyHeaderOffset];
|
|
4258
|
-
[self offsetDiag:[NSString stringWithFormat:@"pager-top-complete-token=%lu-target=%.4f", (unsigned long)_pagerTopToken, _pagerTopLogicalTarget] page:_currentIndex scroll:scrollView];
|
|
4259
4922
|
[self cancelPagerTop];
|
|
4260
4923
|
}
|
|
4261
4924
|
|
|
4262
4925
|
- (void)scrollToTop:(NSInteger)target animated:(BOOL)animated
|
|
4263
4926
|
{
|
|
4927
|
+
[self cancelTopInsetReboundCheck];
|
|
4264
4928
|
UIScrollView *scrollView = _observedScrollView;
|
|
4265
4929
|
NSString *key = _observedPageKey;
|
|
4266
4930
|
if ((target != 0 && target != 1) || _isBeingRecycled || _transitioning || _isPagerDragging ||
|
|
@@ -4292,7 +4956,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4292
4956
|
[self applyHeaderOffset];
|
|
4293
4957
|
CGFloat physicalTarget = RNCLimitRestoredScrollOffset(logicalTarget - scrollView.contentInset.top,
|
|
4294
4958
|
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
4959
|
_dispatchingPagerTop = YES;
|
|
4297
4960
|
[scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x, physicalTarget) animated:animated];
|
|
4298
4961
|
_dispatchingPagerTop = NO;
|
|
@@ -4357,6 +5020,22 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4357
5020
|
: velocity;
|
|
4358
5021
|
BOOL hasMotion = MAX(fabs(intent.x), fabs(intent.y)) >= 1;
|
|
4359
5022
|
BOOL horizontal = hasMotion && fabs(intent.x) > fabs(intent.y);
|
|
5023
|
+
if (_sharedHeaderOuterPagerGesture.protectsNestedBody) {
|
|
5024
|
+
NSInteger pageCount = _pageControllers.count;
|
|
5025
|
+
BOOL validPage = _currentIndex >= 0 && _currentIndex < pageCount;
|
|
5026
|
+
NSInteger firstPageIndex = self.isLtrLayout ? 0 : pageCount - 1;
|
|
5027
|
+
NSInteger lastPageIndex = self.isLtrLayout ? pageCount - 1 : 0;
|
|
5028
|
+
BOOL outward = validPage &&
|
|
5029
|
+
((_currentIndex == firstPageIndex && intent.x > 0) ||
|
|
5030
|
+
(_currentIndex == lastPageIndex && intent.x < 0));
|
|
5031
|
+
UIScrollView *outer = RNCNestedOuterReactPagerScrollView(self);
|
|
5032
|
+
BOOL protectInnerPage = horizontal && validPage && pageCount > 1 &&
|
|
5033
|
+
_nestedScrollEnabled && _scrollEnabled && _pagerScrollView.scrollEnabled &&
|
|
5034
|
+
_pagerScrollView.panGestureRecognizer.enabled && outer != nil && outer.scrollEnabled &&
|
|
5035
|
+
[_sharedHeaderOuterPagerGesture.blockedPagerGestures
|
|
5036
|
+
containsObject:outer.panGestureRecognizer] && !outward;
|
|
5037
|
+
return protectInnerPage;
|
|
5038
|
+
}
|
|
4360
5039
|
RNCCollapsiblePagerLog([NSString stringWithFormat:
|
|
4361
5040
|
@"header-axis role=outer-pager-block dx=%.2f dy=%.2f velocity=(%.2f,%.2f) result=%d outer-pagers=%lu",
|
|
4362
5041
|
intent.x,
|
|
@@ -4396,24 +5075,21 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4396
5075
|
return vertical;
|
|
4397
5076
|
}
|
|
4398
5077
|
if (gestureRecognizer != _blockerGesture) return YES;
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
UIPanGestureRecognizer *pan = (UIPanGestureRecognizer *)gestureRecognizer;
|
|
4403
|
-
CGPoint velocity = [pan velocityInView:self];
|
|
5078
|
+
UIPanGestureRecognizer *pan = [gestureRecognizer isKindOfClass:UIPanGestureRecognizer.class]
|
|
5079
|
+
? (UIPanGestureRecognizer *)gestureRecognizer : nil;
|
|
5080
|
+
CGPoint velocity = pan != nil ? [pan velocityInView:self] : CGPointZero;
|
|
4404
5081
|
NSInteger pageCount = _pageControllers.count;
|
|
4405
|
-
if (pageCount == 0 || _currentIndex < 0 || _currentIndex >= pageCount) return NO;
|
|
4406
|
-
|
|
4407
5082
|
CGFloat absVelocityX = fabs(velocity.x);
|
|
4408
5083
|
CGFloat absVelocityY = fabs(velocity.y);
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
BOOL
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
5084
|
+
NSInteger firstPageIndex = self.isLtrLayout ? 0 : pageCount - 1;
|
|
5085
|
+
NSInteger lastPageIndex = self.isLtrLayout ? pageCount - 1 : 0;
|
|
5086
|
+
BOOL validPage = pageCount > 0 && _currentIndex >= 0 && _currentIndex < pageCount;
|
|
5087
|
+
BOOL horizontal = absVelocityX > absVelocityY && absVelocityX >= 1.0f;
|
|
5088
|
+
BOOL outward = validPage &&
|
|
5089
|
+
((_currentIndex == firstPageIndex && velocity.x > 0) ||
|
|
5090
|
+
(_currentIndex == lastPageIndex && velocity.x < 0));
|
|
5091
|
+
BOOL shouldBegin = _nestedScrollEnabled && pan != nil && horizontal && outward;
|
|
5092
|
+
return shouldBegin;
|
|
4417
5093
|
}
|
|
4418
5094
|
|
|
4419
5095
|
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
|
|
@@ -4423,13 +5099,29 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4423
5099
|
_sharedHeaderPressCancellationGestureHost == nil) return NO;
|
|
4424
5100
|
UIView *touchView = touch.view;
|
|
4425
5101
|
BOOL beginsInSharedHeader = touchView == _sharedHeaderHostView ||
|
|
4426
|
-
[touchView isDescendantOfView:_sharedHeaderHostView]
|
|
4427
|
-
|
|
5102
|
+
[touchView isDescendantOfView:_sharedHeaderHostView] ||
|
|
5103
|
+
touchView == _pinnedStickyHostView ||
|
|
5104
|
+
[touchView isDescendantOfView:_pinnedStickyHostView];
|
|
5105
|
+
if (_sharedHeaderOuterPagerGesture.trackedTouch != nil &&
|
|
5106
|
+
_sharedHeaderOuterPagerGesture.trackedTouch != touch) return NO;
|
|
5107
|
+
UIScrollView *outer = RNCNestedOuterReactPagerScrollView(self);
|
|
5108
|
+
BOOL beginsInNestedBody = !beginsInSharedHeader && _nestedScrollEnabled &&
|
|
5109
|
+
_scrollEnabled && _pagerScrollView.scrollEnabled &&
|
|
5110
|
+
_pagerScrollView.panGestureRecognizer.enabled && _pageControllers.count > 1 &&
|
|
5111
|
+
outer != nil && outer.scrollEnabled &&
|
|
5112
|
+
[_sharedHeaderOuterPagerGesture.blockedPagerGestures containsObject:outer.panGestureRecognizer] &&
|
|
5113
|
+
(touchView == _pagerScrollView || [touchView isDescendantOfView:_pagerScrollView]);
|
|
5114
|
+
if (!beginsInSharedHeader && !beginsInNestedBody) return NO;
|
|
5115
|
+
|
|
5116
|
+
_sharedHeaderOuterPagerGesture.trackedTouch = touch;
|
|
5117
|
+
_sharedHeaderOuterPagerGesture.protectsNestedBody = beginsInNestedBody;
|
|
5118
|
+
_sharedHeaderOuterPagerGesture.cancelsTouchesInView = !beginsInNestedBody;
|
|
4428
5119
|
|
|
4429
5120
|
RNCCollapsiblePagerLog([NSString stringWithFormat:
|
|
4430
|
-
@"outer-pager-touch target=%@ outer-pagers=%lu",
|
|
5121
|
+
@"outer-pager-touch target=%@ outer-pagers=%lu nested-body=%d",
|
|
4431
5122
|
touchView == nil ? @"none" : NSStringFromClass(touchView.class),
|
|
4432
|
-
(unsigned long)_sharedHeaderOuterPagerGesture.blockedPagerGestures.count
|
|
5123
|
+
(unsigned long)_sharedHeaderOuterPagerGesture.blockedPagerGestures.count,
|
|
5124
|
+
beginsInNestedBody]);
|
|
4433
5125
|
return YES;
|
|
4434
5126
|
}
|
|
4435
5127
|
if (gestureRecognizer == _contentPressCancellationGesture) {
|
|
@@ -4438,14 +5130,17 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4438
5130
|
return _nativeSmoothHeaderScrollEnabled && !_isBeingRecycled &&
|
|
4439
5131
|
_contentPressCancellationGesture.view != nil &&
|
|
4440
5132
|
[touchView isDescendantOfView:_pagerScrollView] &&
|
|
4441
|
-
![touchView isDescendantOfView:_sharedHeaderHostView]
|
|
5133
|
+
![touchView isDescendantOfView:_sharedHeaderHostView] &&
|
|
5134
|
+
![touchView isDescendantOfView:_pinnedStickyHostView];
|
|
4442
5135
|
}
|
|
4443
5136
|
if (gestureRecognizer == _sharedHeaderPressCancellationGesture) {
|
|
4444
5137
|
if (!_nativeSmoothHeaderScrollEnabled || _isBeingRecycled ||
|
|
4445
5138
|
_sharedHeaderPressCancellationGestureHost == nil) return NO;
|
|
4446
5139
|
UIView *touchView = touch.view;
|
|
4447
5140
|
BOOL beginsInSharedHeader = touchView == _sharedHeaderHostView ||
|
|
4448
|
-
[touchView isDescendantOfView:_sharedHeaderHostView]
|
|
5141
|
+
[touchView isDescendantOfView:_sharedHeaderHostView] ||
|
|
5142
|
+
touchView == _pinnedStickyHostView ||
|
|
5143
|
+
[touchView isDescendantOfView:_pinnedStickyHostView];
|
|
4449
5144
|
if (!beginsInSharedHeader) return NO;
|
|
4450
5145
|
|
|
4451
5146
|
RNCCollapsiblePagerLog([NSString stringWithFormat:
|
|
@@ -4456,7 +5151,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4456
5151
|
if (gestureRecognizer == _verticalPagerGesture) {
|
|
4457
5152
|
UIView *touchView = touch.view;
|
|
4458
5153
|
return touchView != _sharedHeaderHostView &&
|
|
4459
|
-
![touchView isDescendantOfView:_sharedHeaderHostView]
|
|
5154
|
+
![touchView isDescendantOfView:_sharedHeaderHostView] &&
|
|
5155
|
+
touchView != _pinnedStickyHostView &&
|
|
5156
|
+
![touchView isDescendantOfView:_pinnedStickyHostView];
|
|
4460
5157
|
}
|
|
4461
5158
|
// A new touch can cancel UIKit's animation before its first frame. Queue a
|
|
4462
5159
|
// non-animated settle for the next safe transition boundary instead of
|