@onekeyfe/react-native-pager-view 3.0.170 → 3.0.172
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerHost.kt +283 -21
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerViewManager.kt +8 -0
- package/android/src/main/java/com/reactnativepagerview/NativeScrollerView.kt +2 -0
- package/android/src/test/java/com/reactnativepagerview/NativeScrollerPendingRestoreTest.kt +11 -0
- package/docs/SPEC.md +50 -1
- package/ios/RNCCollapsiblePagerViewComponentView.mm +275 -25
- package/ios/RNCNativeScrollerComponentView.h +1 -0
- package/ios/RNCNativeScrollerComponentView.mm +42 -0
- package/ios/tests/RNCCollapsiblePagerFirstFrameTests.mm +257 -0
- package/package.json +2 -2
package/docs/SPEC.md
CHANGED
|
@@ -3,6 +3,54 @@
|
|
|
3
3
|
Existing PagerView and CollapsiblePagerView APIs are documented in README.md.
|
|
4
4
|
The sections below define native coordination boundaries without new React props.
|
|
5
5
|
|
|
6
|
+
## Smooth per-page screen restoration
|
|
7
|
+
|
|
8
|
+
When nativeSmoothHeaderScrollEnabled is enabled with a collapsible header, each
|
|
9
|
+
page owns its header collapse and body offset. For a settled position outside
|
|
10
|
+
pull overscroll, Q = H + L, H = min(Q, C), and L = max(Q - C, 0), where C is
|
|
11
|
+
the header-collapse distance. These are semantic coordinates, not new props.
|
|
12
|
+
An unvisited page starts expanded
|
|
13
|
+
at body start. Leaving a page preserves its expanded, partial or collapsed header
|
|
14
|
+
and its body position; tap, swipe and programmatic selection restore the same
|
|
15
|
+
complete screen state. Legacy mode and pagers without a collapsible header retain
|
|
16
|
+
their existing behavior.
|
|
17
|
+
|
|
18
|
+
Horizontal progress interpolates the shared header between immutable source and
|
|
19
|
+
target snapshots. Native body geometry follows that header without obscured rows
|
|
20
|
+
or blank gaps. Temporary transition offsets MUST NOT overwrite page snapshots.
|
|
21
|
+
Non-adjacent commands use the original source and destination, not intermediate
|
|
22
|
+
pages, as the header interpolation endpoints. Cancellation restores the source
|
|
23
|
+
and all temporarily compensated pages before observation resumes.
|
|
24
|
+
|
|
25
|
+
A scroll observer owns one page identity and generation independently of mutable
|
|
26
|
+
selection. Detach, replacement and late notifications MUST NOT save another
|
|
27
|
+
page's offset. Zero layout and unresolved replacement identity preserve pending
|
|
28
|
+
restoration until an actual usable layout; settled content shrink still clamps to
|
|
29
|
+
the legal range. This state is native UI-thread state, not persistent wallet data
|
|
30
|
+
or per-frame JavaScript synchronization. Vertical scrolling, refresh, snapping
|
|
31
|
+
and repeated-current-page selection retain their existing contracts.
|
|
32
|
+
|
|
33
|
+
Android owned NativeScroller restoration keeps its intent while a posted request
|
|
34
|
+
has not executed or native content is still too short to reach the saved offset.
|
|
35
|
+
Only the same viewport, page key and attachment generation may reconcile an
|
|
36
|
+
issued request. Once native restoration settles or is cancelled by touch, an
|
|
37
|
+
imperative command or content replacement, the host resumes recording its live
|
|
38
|
+
body offset. Existing pre-draw observation also reconciles a same-position command
|
|
39
|
+
that emits no scroll notification. Zero-sized geometry retains the pending intent;
|
|
40
|
+
no timeout infers that delayed content is permanently short.
|
|
41
|
+
|
|
42
|
+
In smooth-header mode, Android queued RecyclerView and native ScrollView
|
|
43
|
+
restoration requests belong to one page, native view and attachment generation.
|
|
44
|
+
Touch cancellation or replacement MUST invalidate the old request. A stale posted
|
|
45
|
+
callback MUST NOT restore its offset or clear a newer request's pending state.
|
|
46
|
+
|
|
47
|
+
On iOS, a valid scrollTo or scrollToEnd command for the currently observed page
|
|
48
|
+
MUST cancel that page's pre-layout restoration intent before executing, including
|
|
49
|
+
commands whose target is already the current offset. The command takes ownership
|
|
50
|
+
of the live reading position; other pages' pending intent is unchanged. This
|
|
51
|
+
coordination does not apply during horizontal transitions or synthetic alignment,
|
|
52
|
+
and does not change legacy mode or invalid-command behavior.
|
|
53
|
+
|
|
6
54
|
## Independent NativeScroller migration
|
|
7
55
|
|
|
8
56
|
Status: implemented in source. Both native builds pass. Android focused emulator
|
|
@@ -454,7 +502,8 @@ When restoring a retained page's cached vertical offset, the pager limits only
|
|
|
454
502
|
the upper bound to the current content height, viewport and applied insets. A
|
|
455
503
|
cache captured before rows were removed MUST NOT restore an offset below the new
|
|
456
504
|
content bottom, including when removal occurs during a page transition. Valid
|
|
457
|
-
deep offsets and
|
|
505
|
+
deep offsets and pull-overscroll behavior are preserved; smooth-header selection
|
|
506
|
+
follows the per-page reading-position contract above.
|
|
458
507
|
This check runs only on explicit restoration, not on scroll frames; UIKit pull
|
|
459
508
|
overscroll and refresh settlement retain their existing ownership. An unmeasured
|
|
460
509
|
zero-height viewport defers this upper-bound limit to a later restoration.
|
|
@@ -1437,6 +1437,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1437
1437
|
@end
|
|
1438
1438
|
|
|
1439
1439
|
@implementation RNCCollapsiblePagerViewComponentView {
|
|
1440
|
+
#if DEBUG
|
|
1441
|
+
NSUInteger _offsetDiagSequence;
|
|
1442
|
+
#endif
|
|
1440
1443
|
UIView *_containerView;
|
|
1441
1444
|
RNCCollapsiblePagerSharedHeaderView *_sharedHeaderHostView;
|
|
1442
1445
|
UIPageViewController *_pageViewController;
|
|
@@ -1491,6 +1494,18 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1491
1494
|
BOOL _needsScrollObserverReattachAfterMount;
|
|
1492
1495
|
NSHashTable<UIScrollView *> *_mountingRemovedScrollViews;
|
|
1493
1496
|
__weak UIScrollView *_observedScrollView;
|
|
1497
|
+
NSString *_observedPageKey;
|
|
1498
|
+
NSInteger _observedPageIndex;
|
|
1499
|
+
NSUInteger _observedPageGeneration;
|
|
1500
|
+
NSDictionary<NSString *, NSNumber *> *_transitionPageOffsets;
|
|
1501
|
+
NSMutableIndexSet *_transitionCompensatedPages;
|
|
1502
|
+
#if DEBUG
|
|
1503
|
+
NSInteger _transitionDiagBucket;
|
|
1504
|
+
#endif
|
|
1505
|
+
NSInteger _transitionSourceIndex;
|
|
1506
|
+
BOOL _syntheticPageRestore;
|
|
1507
|
+
BOOL _finishingPageStateTransition;
|
|
1508
|
+
NSMutableSet<NSString *> *_pendingPageRestoreKeys;
|
|
1494
1509
|
__weak UIScrollView *_sharedHeaderScrollView;
|
|
1495
1510
|
__weak UIScrollView *_pendingFallbackScrollView;
|
|
1496
1511
|
__weak UIView *_sharedHeaderPressCancellationGestureHost;
|
|
@@ -1525,6 +1540,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1525
1540
|
_logicalChildren = [NSMutableArray new];
|
|
1526
1541
|
_pageControllers = [NSMutableArray new];
|
|
1527
1542
|
_pageOffsets = [NSMutableDictionary new];
|
|
1543
|
+
_pendingPageRestoreKeys = [NSMutableSet new];
|
|
1528
1544
|
_releasedPageScrollStates = [NSMutableDictionary new];
|
|
1529
1545
|
_mountingRemovedScrollViews = [NSHashTable weakObjectsHashTable];
|
|
1530
1546
|
_originalInsets = [NSMapTable weakToStrongObjectsMapTable];
|
|
@@ -1899,18 +1915,123 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1899
1915
|
[self applyHeaderOffset];
|
|
1900
1916
|
}
|
|
1901
1917
|
|
|
1918
|
+
- (void)offsetDiag:(NSString *)reason page:(NSInteger)index scroll:(UIScrollView *)scroll
|
|
1919
|
+
{
|
|
1920
|
+
#if DEBUG
|
|
1921
|
+
NSString *key = index >= 0 && index < _pageControllers.count ? [self pageKeyForIndex:index] : @"none";
|
|
1922
|
+
if (![@[@"portfolio", @"perps", @"defi", @"nft", @"history"] containsObject:key]) key = @"other";
|
|
1923
|
+
NSNumber *saved = index >= 0 && index < _pageControllers.count ? _pageOffsets[[self pageKeyForIndex:index]] : nil;
|
|
1924
|
+
UIView *component = scroll == nil ? nil : RNCScrollComponentView(scroll);
|
|
1925
|
+
RNCCollapsiblePagerLog([NSString stringWithFormat:
|
|
1926
|
+
@"[HOME-OFFSET-DIAG] pager phase=%@ seq=%lu wallMs=%.0f mono=%.6f host=%p generation=%lu transitionId=%lu page=%ld key=%@ current=%ld destination=%ld transition=%d dragging=%d scroll=%p tag=%ld component=%p componentTag=%ld observed=%p ownerIndex=%ld ownerGeneration=%lu synthetic=%d cacheExists=%d cache=%.4f header=%.4f headerHeight=%ld physicalY=%.4f insetTop=%.4f insetBottom=%.4f logicalY=%.4f contentHeight=%.4f boundsHeight=%.4f",
|
|
1927
|
+
reason, (unsigned long)++_offsetDiagSequence, NSDate.date.timeIntervalSince1970 * 1000, CACurrentMediaTime(),
|
|
1928
|
+
(__bridge void *)self, (unsigned long)_generation, (unsigned long)_transitionId, (long)index, key,
|
|
1929
|
+
(long)_currentIndex, (long)_destinationIndex, _transitioning, _isPagerDragging,
|
|
1930
|
+
(__bridge void *)scroll, (long)scroll.tag, (__bridge void *)component, (long)component.tag,
|
|
1931
|
+
(__bridge void *)_observedScrollView, (long)_observedPageIndex, (unsigned long)_observedPageGeneration, _syntheticPageRestore, saved != nil, saved.doubleValue, _headerOffset, (long)_headerHeight,
|
|
1932
|
+
scroll.contentOffset.y, scroll.contentInset.top, scroll.contentInset.bottom,
|
|
1933
|
+
scroll.contentOffset.y + scroll.contentInset.top, scroll.contentSize.height, scroll.bounds.size.height]);
|
|
1934
|
+
#endif
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1902
1937
|
- (void)preparePageForHorizontalTransitionAtIndex:(NSInteger)pageIndex
|
|
1903
1938
|
{
|
|
1904
|
-
|
|
1939
|
+
[self offsetDiag:@"prepare-check" page:pageIndex scroll:nil];
|
|
1940
|
+
if (!_nativeSmoothHeaderScrollEnabled ||
|
|
1905
1941
|
pageIndex < 0 || pageIndex >= _pageControllers.count) return;
|
|
1906
|
-
|
|
1907
|
-
_pageOffsets[[self pageKeyForIndex:pageIndex]] = @(_headerOffset);
|
|
1908
1942
|
UIScrollView *scrollView = [self verticalScrollViewForPageAtIndex:pageIndex];
|
|
1909
1943
|
if (scrollView != nil && scrollView != _observedScrollView) {
|
|
1910
1944
|
[self applyInsetsToScrollView:scrollView pageIndex:pageIndex restore:YES];
|
|
1911
1945
|
}
|
|
1912
1946
|
}
|
|
1913
1947
|
|
|
1948
|
+
- (void)beginPageStateTransition
|
|
1949
|
+
{
|
|
1950
|
+
if (!_nativeSmoothHeaderScrollEnabled || _headerHeight <= 0 || _transitionPageOffsets != nil) return;
|
|
1951
|
+
[self detachScrollObserver];
|
|
1952
|
+
_transitionSourceIndex = _currentIndex;
|
|
1953
|
+
_transitionPageOffsets = [_pageOffsets copy];
|
|
1954
|
+
_transitionCompensatedPages = [NSMutableIndexSet new];
|
|
1955
|
+
#if DEBUG
|
|
1956
|
+
_transitionDiagBucket = -1;
|
|
1957
|
+
[self offsetDiag:[NSString stringWithFormat:@"screen-begin-source=%ld-savedQ=%.4f", (long)_transitionSourceIndex,
|
|
1958
|
+
[_transitionPageOffsets[self.currentPageKey] doubleValue]] page:_currentIndex scroll:nil];
|
|
1959
|
+
#endif
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
- (void)updatePageStateTransitionProgress:(CGFloat)progress
|
|
1963
|
+
{
|
|
1964
|
+
if (_transitionPageOffsets == nil || _finishingPageStateTransition) return;
|
|
1965
|
+
NSInteger first = MAX(0, MIN((NSInteger)floor(progress), (NSInteger)_pageControllers.count - 1));
|
|
1966
|
+
NSInteger second = MIN(first + 1, (NSInteger)_pageControllers.count - 1);
|
|
1967
|
+
CGFloat fraction = RNCClamp(progress - first, 0, 1);
|
|
1968
|
+
if (!_isPagerDragging && _destinationIndex != _transitionSourceIndex) {
|
|
1969
|
+
first = _transitionSourceIndex;
|
|
1970
|
+
second = _destinationIndex;
|
|
1971
|
+
fraction = RNCClamp((progress - first) / (second - first), 0, 1);
|
|
1972
|
+
}
|
|
1973
|
+
CGFloat firstQ = [_transitionPageOffsets[[self pageKeyForIndex:first]] doubleValue];
|
|
1974
|
+
CGFloat secondQ = [_transitionPageOffsets[[self pageKeyForIndex:second]] doubleValue];
|
|
1975
|
+
CGFloat firstH = RNCClamp(firstQ, 0, _headerHeight);
|
|
1976
|
+
CGFloat secondH = RNCClamp(secondQ, 0, _headerHeight);
|
|
1977
|
+
_headerOffset = firstH + (secondH - firstH) * fraction;
|
|
1978
|
+
#if DEBUG
|
|
1979
|
+
NSInteger bucket = (NSInteger)floor(fraction * 4);
|
|
1980
|
+
if (_transitionDiagBucket != bucket) {
|
|
1981
|
+
_transitionDiagBucket = bucket;
|
|
1982
|
+
[self offsetDiag:[NSString stringWithFormat:
|
|
1983
|
+
@"screen-progress-kind=%@-source=%ld-target=%ld-sourceQ=%.4f-targetQ=%.4f-position=%.4f-fraction=%.4f",
|
|
1984
|
+
_isPagerDragging ? @"interactive" : @"programmatic-endpoint", (long)first, (long)second,
|
|
1985
|
+
firstQ, secondQ, progress, fraction] page:_currentIndex scroll:nil];
|
|
1986
|
+
}
|
|
1987
|
+
#endif
|
|
1988
|
+
_syntheticPageRestore = YES;
|
|
1989
|
+
NSInteger visibleFirst = MAX(0, MIN((NSInteger)floor(progress), (NSInteger)_pageControllers.count - 1));
|
|
1990
|
+
NSInteger visibleLast = MIN(visibleFirst + 1, (NSInteger)_pageControllers.count - 1);
|
|
1991
|
+
NSMutableIndexSet *displayPages = [NSMutableIndexSet indexSetWithIndexesInRange:
|
|
1992
|
+
NSMakeRange(visibleFirst, visibleLast - visibleFirst + 1)];
|
|
1993
|
+
if (!_isPagerDragging) {
|
|
1994
|
+
// UIView tap animation reports its endpoint once while the source exits.
|
|
1995
|
+
[displayPages addIndex:_transitionSourceIndex];
|
|
1996
|
+
[displayPages addIndex:_destinationIndex];
|
|
1997
|
+
}
|
|
1998
|
+
for (NSUInteger index = displayPages.firstIndex; index != NSNotFound;
|
|
1999
|
+
index = [displayPages indexGreaterThanIndex:index]) {
|
|
2000
|
+
UIScrollView *scroll = [self verticalScrollViewForPageAtIndex:index];
|
|
2001
|
+
if (scroll != nil) [self applyInsetsToScrollView:scroll pageIndex:index restore:YES];
|
|
2002
|
+
}
|
|
2003
|
+
_syntheticPageRestore = NO;
|
|
2004
|
+
[self layoutSharedHeaders];
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
- (void)finishPageStateTransition
|
|
2008
|
+
{
|
|
2009
|
+
if (_transitionPageOffsets == nil) return;
|
|
2010
|
+
_finishingPageStateTransition = YES;
|
|
2011
|
+
_headerOffset = RNCClamp([_transitionPageOffsets[self.currentPageKey] doubleValue], 0, _headerHeight);
|
|
2012
|
+
_syntheticPageRestore = YES;
|
|
2013
|
+
// Both completion and cancellation undo every temporary viewport offset.
|
|
2014
|
+
for (NSUInteger index = _transitionCompensatedPages.firstIndex; index != NSNotFound;
|
|
2015
|
+
index = [_transitionCompensatedPages indexGreaterThanIndex:index]) {
|
|
2016
|
+
UIScrollView *scroll = [self verticalScrollViewForPageAtIndex:index];
|
|
2017
|
+
if (scroll == nil) continue;
|
|
2018
|
+
[self applyInsetsToScrollView:scroll pageIndex:index restore:YES];
|
|
2019
|
+
NSString *key = [self pageKeyForIndex:index];
|
|
2020
|
+
if (![_pendingPageRestoreKeys containsObject:key]) {
|
|
2021
|
+
_pageOffsets[key] = @(scroll.contentOffset.y + scroll.contentInset.top);
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
_syntheticPageRestore = NO;
|
|
2025
|
+
_finishingPageStateTransition = NO;
|
|
2026
|
+
_transitionPageOffsets = nil;
|
|
2027
|
+
_transitionCompensatedPages = nil;
|
|
2028
|
+
[self layoutSharedHeaders];
|
|
2029
|
+
#if DEBUG
|
|
2030
|
+
[self offsetDiag:[NSString stringWithFormat:@"screen-finish-source=%ld-cancelled=%d", (long)_transitionSourceIndex,
|
|
2031
|
+
_currentIndex == _transitionSourceIndex] page:_currentIndex scroll:nil];
|
|
2032
|
+
#endif
|
|
2033
|
+
}
|
|
2034
|
+
|
|
1914
2035
|
#pragma mark - React mounting
|
|
1915
2036
|
|
|
1916
2037
|
// OneKey patch: Fabric can recycle a removed list as a descendant of our header
|
|
@@ -1958,8 +2079,16 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1958
2079
|
_releasedPageScrollStates[pageKey] = state;
|
|
1959
2080
|
// Retained pages may be visually aligned to the expanded header while
|
|
1960
2081
|
// their saved offsets still hold the user's independent list position.
|
|
1961
|
-
|
|
2082
|
+
BOOL ownsObservedPage = scrollView == _observedScrollView &&
|
|
2083
|
+
(!_nativeSmoothHeaderScrollEnabled ||
|
|
2084
|
+
(_observedPageGeneration == _generation && _observedPageIndex == index &&
|
|
2085
|
+
[_observedPageKey isEqualToString:pageKey]));
|
|
2086
|
+
if (_transitionPageOffsets == nil && !_syntheticPageRestore &&
|
|
2087
|
+
(!_nativeSmoothHeaderScrollEnabled || ![_pendingPageRestoreKeys containsObject:pageKey]) &&
|
|
2088
|
+
(ownsObservedPage || _pageOffsets[pageKey] == nil)) {
|
|
2089
|
+
[self offsetDiag:@"mount-release-save-before" page:index scroll:scrollView];
|
|
1962
2090
|
_pageOffsets[pageKey] = @(scrollView.contentOffset.y + scrollView.contentInset.top);
|
|
2091
|
+
[self offsetDiag:@"mount-release-save-after" page:index scroll:scrollView];
|
|
1963
2092
|
}
|
|
1964
2093
|
break;
|
|
1965
2094
|
}
|
|
@@ -2243,7 +2372,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2243
2372
|
if (appliedTop != nil && scrollView.refreshControl != nil) {
|
|
2244
2373
|
original.top += MAX(0, scrollView.contentInset.top - appliedTop.doubleValue);
|
|
2245
2374
|
}
|
|
2246
|
-
|
|
2375
|
+
[self offsetDiag:@"release-original-inset-before" page:_currentIndex scroll:scrollView];
|
|
2376
|
+
scrollView.contentInset = original;
|
|
2377
|
+
[self offsetDiag:@"release-original-inset-after" page:_currentIndex scroll:scrollView];
|
|
2247
2378
|
}
|
|
2248
2379
|
NSValue *indicatorValue = [_originalIndicatorInsets objectForKey:scrollView];
|
|
2249
2380
|
if (indicatorValue != nil) {
|
|
@@ -2295,6 +2426,11 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2295
2426
|
_pagerScrollView = nil;
|
|
2296
2427
|
[super prepareForRecycle];
|
|
2297
2428
|
[_pageOffsets removeAllObjects];
|
|
2429
|
+
[_pendingPageRestoreKeys removeAllObjects];
|
|
2430
|
+
_transitionPageOffsets = nil;
|
|
2431
|
+
_transitionCompensatedPages = nil;
|
|
2432
|
+
_syntheticPageRestore = NO;
|
|
2433
|
+
_finishingPageStateTransition = NO;
|
|
2298
2434
|
_pageKeys = @[];
|
|
2299
2435
|
_retainedPages = @"[]";
|
|
2300
2436
|
_currentIndex = 0;
|
|
@@ -2627,6 +2763,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2627
2763
|
- (void)attachScrollObserverForCurrentPageAfterTransition:(BOOL)selectionSettled
|
|
2628
2764
|
{
|
|
2629
2765
|
if (_isBeingRecycled) return;
|
|
2766
|
+
if (_nativeSmoothHeaderScrollEnabled &&
|
|
2767
|
+
(_transitioning || _isPagerDragging || _transitionPageOffsets != nil) && !selectionSettled) return;
|
|
2768
|
+
if (selectionSettled) [self finishPageStateTransition];
|
|
2630
2769
|
if (_needsScrollObserverReattachAfterMount) {
|
|
2631
2770
|
if (self.window == nil || ((_transitioning || _isPagerDragging) && !selectionSettled)) return;
|
|
2632
2771
|
[self prepareAdjacentPageInsets];
|
|
@@ -2685,20 +2824,34 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2685
2824
|
if (_pagerScrollView != nil && !_nativeSmoothHeaderScrollEnabled && !_needsPropsReapply) {
|
|
2686
2825
|
[candidate.panGestureRecognizer requireGestureRecognizerToFail:_pagerScrollView.panGestureRecognizer];
|
|
2687
2826
|
}
|
|
2688
|
-
if (candidate == _observedScrollView
|
|
2827
|
+
if (candidate == _observedScrollView &&
|
|
2828
|
+
(!_nativeSmoothHeaderScrollEnabled ||
|
|
2829
|
+
([_observedPageKey isEqualToString:self.currentPageKey] &&
|
|
2830
|
+
_observedPageIndex == _currentIndex && _observedPageGeneration == _generation))) {
|
|
2689
2831
|
_needsScrollObserverReattachAfterMount = NO;
|
|
2832
|
+
if ([_pendingPageRestoreKeys containsObject:self.currentPageKey]) {
|
|
2833
|
+
[self applyInsetsToScrollView:candidate pageIndex:_currentIndex restore:YES];
|
|
2834
|
+
}
|
|
2690
2835
|
[self attachSharedHeadersToScrollView:candidate];
|
|
2691
2836
|
[self updateSharedHeaderPressCancellationGesture];
|
|
2692
2837
|
return;
|
|
2693
2838
|
}
|
|
2694
2839
|
|
|
2695
|
-
|
|
2840
|
+
[self offsetDiag:@"observer-candidate" page:_currentIndex scroll:candidate];
|
|
2841
|
+
BOOL replacingCurrentScrollView = _observingContentOffset &&
|
|
2842
|
+
(!_nativeSmoothHeaderScrollEnabled || [_observedPageKey isEqualToString:self.currentPageKey]);
|
|
2696
2843
|
[self detachScrollObserver];
|
|
2697
2844
|
if (replacingCurrentScrollView) {
|
|
2698
|
-
//
|
|
2699
|
-
|
|
2845
|
+
// Legacy replacement discards body state; smooth replacement waits for layout.
|
|
2846
|
+
[self offsetDiag:@"observer-replacement-cache-before" page:_currentIndex scroll:candidate];
|
|
2847
|
+
if (!_nativeSmoothHeaderScrollEnabled) _pageOffsets[self.currentPageKey] = @(_headerOffset);
|
|
2848
|
+
[self offsetDiag:@"observer-replacement-cache-after" page:_currentIndex scroll:candidate];
|
|
2700
2849
|
}
|
|
2701
2850
|
_observedScrollView = candidate;
|
|
2851
|
+
_observedPageKey = [self.currentPageKey copy];
|
|
2852
|
+
_observedPageIndex = _currentIndex;
|
|
2853
|
+
_observedPageGeneration = _generation;
|
|
2854
|
+
[self offsetDiag:@"observer-attached-before-restore" page:_currentIndex scroll:candidate];
|
|
2702
2855
|
[self applyInsetsToScrollView:candidate pageIndex:_currentIndex restore:YES];
|
|
2703
2856
|
[candidate addObserver:self
|
|
2704
2857
|
forKeyPath:@"contentOffset"
|
|
@@ -2725,7 +2878,17 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2725
2878
|
UIScrollView *scrollView = _observedScrollView;
|
|
2726
2879
|
if (scrollView != nil) {
|
|
2727
2880
|
[scrollView.panGestureRecognizer removeTarget:self action:@selector(observedListPanChanged:)];
|
|
2728
|
-
|
|
2881
|
+
[self offsetDiag:@"detach-save-before" page:_currentIndex scroll:scrollView];
|
|
2882
|
+
NSString *ownerKey = _nativeSmoothHeaderScrollEnabled ? _observedPageKey : self.currentPageKey;
|
|
2883
|
+
BOOL ownerKeyExists = ownerKey != nil && ([_pageKeys containsObject:ownerKey] ||
|
|
2884
|
+
(_observedPageIndex >= 0 && _observedPageIndex < _pageControllers.count &&
|
|
2885
|
+
[[self pageKeyForIndex:_observedPageIndex] isEqualToString:ownerKey]));
|
|
2886
|
+
if (ownerKey != nil && (!_nativeSmoothHeaderScrollEnabled ||
|
|
2887
|
+
(ownerKeyExists && _observedPageGeneration == _generation && _transitionPageOffsets == nil &&
|
|
2888
|
+
!_syntheticPageRestore && ![_pendingPageRestoreKeys containsObject:ownerKey]))) {
|
|
2889
|
+
_pageOffsets[ownerKey] = @(scrollView.contentOffset.y + scrollView.contentInset.top);
|
|
2890
|
+
}
|
|
2891
|
+
[self offsetDiag:@"detach-save-after" page:_currentIndex scroll:scrollView];
|
|
2729
2892
|
if (_observingContentOffset) {
|
|
2730
2893
|
[scrollView removeObserver:self
|
|
2731
2894
|
forKeyPath:@"contentOffset"
|
|
@@ -2740,6 +2903,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2740
2903
|
}
|
|
2741
2904
|
_observingContentOffset = NO;
|
|
2742
2905
|
_observedScrollView = nil;
|
|
2906
|
+
_observedPageKey = nil;
|
|
2907
|
+
_observedPageIndex = NSNotFound;
|
|
2743
2908
|
if ([scrollView.superview isKindOfClass:RNCNativeScrollerComponentView.class]) {
|
|
2744
2909
|
[(RNCNativeScrollerComponentView *)scrollView.superview stopScrolling];
|
|
2745
2910
|
}
|
|
@@ -3010,7 +3175,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3010
3175
|
[self releaseRefreshControlForScrollView:scrollView];
|
|
3011
3176
|
if (scrollView == _observedScrollView) {
|
|
3012
3177
|
[self detachScrollObserver];
|
|
3013
|
-
|
|
3178
|
+
[self offsetDiag:resetSavedOffset ? @"release-reset-before" : @"release-retain-before" page:_currentIndex scroll:scrollView];
|
|
3179
|
+
if (resetSavedOffset && !_nativeSmoothHeaderScrollEnabled) _pageOffsets[self.currentPageKey] = @(_headerOffset);
|
|
3180
|
+
[self offsetDiag:@"release-reset-after" page:_currentIndex scroll:scrollView];
|
|
3014
3181
|
}
|
|
3015
3182
|
if (scrollView == _sharedHeaderScrollView) {
|
|
3016
3183
|
[self restoreSharedHeadersToContainer];
|
|
@@ -3030,8 +3197,10 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3030
3197
|
[_originalDirectionalLockEnabled objectForKey:scrollView].boolValue;
|
|
3031
3198
|
scrollView.contentInsetAdjustmentBehavior = (UIScrollViewContentInsetAdjustmentBehavior)
|
|
3032
3199
|
[_originalInsetAdjustmentBehavior objectForKey:scrollView].integerValue;
|
|
3200
|
+
[self offsetDiag:@"release-original-offset-before" page:_currentIndex scroll:scrollView];
|
|
3033
3201
|
[scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x, logicalOffset - original.top)
|
|
3034
3202
|
animated:NO];
|
|
3203
|
+
[self offsetDiag:@"release-original-offset-after" page:_currentIndex scroll:scrollView];
|
|
3035
3204
|
[_originalInsets removeObjectForKey:scrollView];
|
|
3036
3205
|
[_appliedTopInsets removeObjectForKey:scrollView];
|
|
3037
3206
|
[_originalIndicatorInsets removeObjectForKey:scrollView];
|
|
@@ -3060,8 +3229,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3060
3229
|
pageIndex:(NSInteger)pageIndex
|
|
3061
3230
|
restore:(BOOL)restore
|
|
3062
3231
|
{
|
|
3063
|
-
//
|
|
3064
|
-
//
|
|
3232
|
+
// Smooth pages retain their complete offset while replacement identity and
|
|
3233
|
+
// layout settle. Legacy pages retain the historical header-only policy.
|
|
3234
|
+
[self offsetDiag:restore ? @"restore-enter" : @"insets-enter" page:pageIndex scroll:scrollView];
|
|
3065
3235
|
NSString *pageKey = [self pageKeyForIndex:pageIndex];
|
|
3066
3236
|
RNCCollapsiblePagerReleasedScrollState *released = _releasedPageScrollStates[pageKey];
|
|
3067
3237
|
BOOL deferredReleasedIdentity = NO;
|
|
@@ -3107,8 +3277,13 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3107
3277
|
deferredReleasedIdentity = now - released.pendingFallbackDetectedAt <
|
|
3108
3278
|
RNCCollapsiblePagerFallbackDebounceInterval;
|
|
3109
3279
|
}
|
|
3280
|
+
#if DEBUG
|
|
3281
|
+
[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];
|
|
3282
|
+
#endif
|
|
3110
3283
|
if (!deferredReleasedIdentity) {
|
|
3111
|
-
|
|
3284
|
+
[self offsetDiag:@"released-identity-cache-before" page:pageIndex scroll:scrollView];
|
|
3285
|
+
if (!sameList && !_nativeSmoothHeaderScrollEnabled) _pageOffsets[pageKey] = @(_headerOffset);
|
|
3286
|
+
[self offsetDiag:@"released-identity-cache-after" page:pageIndex scroll:scrollView];
|
|
3112
3287
|
[_releasedPageScrollStates removeObjectForKey:pageKey];
|
|
3113
3288
|
}
|
|
3114
3289
|
}
|
|
@@ -3156,7 +3331,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3156
3331
|
if (pageIndex == _currentIndex) [self updateHeaderForScrollView:scrollView];
|
|
3157
3332
|
return;
|
|
3158
3333
|
}
|
|
3334
|
+
[self offsetDiag:@"inset-assignment-before" page:pageIndex scroll:scrollView];
|
|
3159
3335
|
scrollView.contentInset = next;
|
|
3336
|
+
[self offsetDiag:@"inset-assignment-after" page:pageIndex scroll:scrollView];
|
|
3160
3337
|
UIEdgeInsets indicatorInsets = scrollView.verticalScrollIndicatorInsets;
|
|
3161
3338
|
indicatorInsets.top = pagerTopInset;
|
|
3162
3339
|
scrollView.verticalScrollIndicatorInsets = indicatorInsets;
|
|
@@ -3164,21 +3341,37 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3164
3341
|
|
|
3165
3342
|
CGFloat targetOffset = previousLogicalOffset - next.top;
|
|
3166
3343
|
if (restore) {
|
|
3167
|
-
NSNumber *saved =
|
|
3344
|
+
NSNumber *saved = _transitionPageOffsets != nil
|
|
3345
|
+
? _transitionPageOffsets[pageKey] : _pageOffsets[pageKey];
|
|
3168
3346
|
// The sticky slot can change height between pages. Cache offsets relative
|
|
3169
3347
|
// to content start so a round trip does not add that height difference.
|
|
3170
|
-
CGFloat restoredLogicalOffset
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3348
|
+
CGFloat restoredLogicalOffset;
|
|
3349
|
+
if (_nativeSmoothHeaderScrollEnabled && _headerHeight > 0) {
|
|
3350
|
+
restoredLogicalOffset = saved.doubleValue;
|
|
3351
|
+
if (_transitionPageOffsets != nil && !_finishingPageStateTransition) {
|
|
3352
|
+
[_transitionCompensatedPages addIndex:pageIndex];
|
|
3353
|
+
restoredLogicalOffset = MAX(restoredLogicalOffset - _headerHeight, 0) + _headerOffset;
|
|
3354
|
+
}
|
|
3355
|
+
if (deferredReleasedIdentity || scrollView.bounds.size.height <= 0 || scrollView.contentSize.height <= 0) {
|
|
3356
|
+
[_pendingPageRestoreKeys addObject:pageKey];
|
|
3357
|
+
return;
|
|
3358
|
+
}
|
|
3359
|
+
[_pendingPageRestoreKeys removeObject:pageKey];
|
|
3360
|
+
} else {
|
|
3361
|
+
restoredLogicalOffset = deferredReleasedIdentity || saved == nil ? _headerOffset : saved.doubleValue;
|
|
3177
3362
|
}
|
|
3363
|
+
#if DEBUG
|
|
3364
|
+
[self offsetDiag:[NSString stringWithFormat:@"restore-candidate-saved=%.4f-headerAlignedLogical=%.4f", saved.doubleValue, restoredLogicalOffset] page:pageIndex scroll:scrollView];
|
|
3365
|
+
#endif
|
|
3178
3366
|
targetOffset = restoredLogicalOffset - next.top;
|
|
3179
|
-
|
|
3367
|
+
if (!_nativeSmoothHeaderScrollEnabled || _headerHeight <= 0) {
|
|
3368
|
+
targetOffset = MAX(targetOffset, -next.top + _headerOffset);
|
|
3369
|
+
}
|
|
3180
3370
|
// An inactive list may shrink after its offset observer was detached.
|
|
3181
3371
|
// Do not overwrite its corrected offset with a now-unreachable cached one.
|
|
3372
|
+
#if DEBUG
|
|
3373
|
+
[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];
|
|
3374
|
+
#endif
|
|
3182
3375
|
targetOffset = RNCLimitRestoredScrollOffset(
|
|
3183
3376
|
targetOffset,
|
|
3184
3377
|
scrollView.contentSize.height,
|
|
@@ -3187,11 +3380,15 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3187
3380
|
next.bottom
|
|
3188
3381
|
);
|
|
3189
3382
|
}
|
|
3383
|
+
#if DEBUG
|
|
3384
|
+
[self offsetDiag:[NSString stringWithFormat:@"restore-final-target=%.4f-restore=%d", targetOffset, restore] page:pageIndex scroll:scrollView];
|
|
3385
|
+
#endif
|
|
3190
3386
|
// A bottom-inset change must not cancel UIKit's refresh or bounce settlement.
|
|
3191
3387
|
if (restore || previousTopInset != next.top) {
|
|
3192
3388
|
[scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x, targetOffset) animated:NO];
|
|
3193
3389
|
}
|
|
3194
3390
|
if (pageIndex == _currentIndex) [self updateHeaderForScrollView:scrollView];
|
|
3391
|
+
[self offsetDiag:@"restore-post" page:pageIndex scroll:scrollView];
|
|
3195
3392
|
}
|
|
3196
3393
|
|
|
3197
3394
|
- (void)updateNativeScrollerViewport:(UIScrollView *)scrollView stickyHeight:(CGFloat)height
|
|
@@ -3201,6 +3398,25 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3201
3398
|
}
|
|
3202
3399
|
}
|
|
3203
3400
|
|
|
3401
|
+
- (void)nativeScrollerWillReceiveScrollCommand:(RNCNativeScrollerComponentView *)scroller
|
|
3402
|
+
{
|
|
3403
|
+
UIScrollView *scrollView = scroller.scrollView;
|
|
3404
|
+
NSString *ownerKey = _observedPageKey;
|
|
3405
|
+
if (!_nativeSmoothHeaderScrollEnabled || scrollView != _observedScrollView ||
|
|
3406
|
+
ownerKey == nil || ![ownerKey isEqualToString:self.currentPageKey] ||
|
|
3407
|
+
_observedPageIndex != _currentIndex || _currentIndex < 0 || _currentIndex >= _pageControllers.count ||
|
|
3408
|
+
_observedPageGeneration != _generation || _transitioning || _isPagerDragging ||
|
|
3409
|
+
_transitionPageOffsets != nil || _syntheticPageRestore ||
|
|
3410
|
+
![_pendingPageRestoreKeys containsObject:ownerKey]) return;
|
|
3411
|
+
[self offsetDiag:@"command-pending-cancel-before" page:_observedPageIndex scroll:scrollView];
|
|
3412
|
+
[_pendingPageRestoreKeys removeObject:ownerKey];
|
|
3413
|
+
_currentLogicalOffset = scrollView.contentOffset.y + scrollView.contentInset.top;
|
|
3414
|
+
_pageOffsets[ownerKey] = @(_currentLogicalOffset);
|
|
3415
|
+
[self updateHeaderForScrollView:scrollView];
|
|
3416
|
+
[self offsetDiag:@"command-pending-cancel-after" page:_observedPageIndex scroll:scrollView];
|
|
3417
|
+
}
|
|
3418
|
+
|
|
3419
|
+
|
|
3204
3420
|
- (void)nativeScroller:(RNCNativeScrollerComponentView *)scroller
|
|
3205
3421
|
callerInsetChangedFrom:(UIEdgeInsets)previous to:(UIEdgeInsets)next
|
|
3206
3422
|
{
|
|
@@ -3252,10 +3468,24 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3252
3468
|
if (context == RNCScrollRefreshControlContext) {
|
|
3253
3469
|
UIScrollView *scrollView = (UIScrollView *)object;
|
|
3254
3470
|
[self updateRefreshControlForScrollView:scrollView];
|
|
3255
|
-
if (scrollView == _observedScrollView
|
|
3471
|
+
if (scrollView == _observedScrollView && !_syntheticPageRestore &&
|
|
3472
|
+
(!_nativeSmoothHeaderScrollEnabled || (!_transitioning && _transitionPageOffsets == nil))) {
|
|
3473
|
+
[self updateHeaderForScrollView:scrollView];
|
|
3474
|
+
}
|
|
3256
3475
|
return;
|
|
3257
3476
|
}
|
|
3258
3477
|
if (context == RNCCollapsiblePagerContentOffsetContext && object == _observedScrollView) {
|
|
3478
|
+
if (_nativeSmoothHeaderScrollEnabled &&
|
|
3479
|
+
(_observedPageGeneration != _generation || _observedPageKey == nil ||
|
|
3480
|
+
_observedPageIndex != _currentIndex || ![_observedPageKey isEqualToString:self.currentPageKey] ||
|
|
3481
|
+
_transitioning || _isPagerDragging ||
|
|
3482
|
+
_transitionPageOffsets != nil || _syntheticPageRestore)) return;
|
|
3483
|
+
if (_nativeSmoothHeaderScrollEnabled && [_pendingPageRestoreKeys containsObject:_observedPageKey]) {
|
|
3484
|
+
if ([keyPath isEqualToString:@"contentSize"]) {
|
|
3485
|
+
[self applyInsetsToScrollView:_observedScrollView pageIndex:_observedPageIndex restore:YES];
|
|
3486
|
+
}
|
|
3487
|
+
return;
|
|
3488
|
+
}
|
|
3259
3489
|
if ([keyPath isEqualToString:@"contentInset"]) {
|
|
3260
3490
|
[self updateHeaderForScrollView:(UIScrollView *)object];
|
|
3261
3491
|
return;
|
|
@@ -3270,7 +3500,14 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3270
3500
|
}
|
|
3271
3501
|
UIScrollView *scrollView = (UIScrollView *)object;
|
|
3272
3502
|
_currentLogicalOffset = scrollView.contentOffset.y + scrollView.contentInset.top;
|
|
3273
|
-
|
|
3503
|
+
#if DEBUG
|
|
3504
|
+
BOOL offsetDiagChanged = _pageOffsets[self.currentPageKey] == nil || _pageOffsets[self.currentPageKey].doubleValue != _currentLogicalOffset;
|
|
3505
|
+
if (offsetDiagChanged) [self offsetDiag:@"kvo-cache-before" page:_currentIndex scroll:scrollView];
|
|
3506
|
+
#endif
|
|
3507
|
+
_pageOffsets[_nativeSmoothHeaderScrollEnabled ? _observedPageKey : self.currentPageKey] = @(_currentLogicalOffset);
|
|
3508
|
+
#if DEBUG
|
|
3509
|
+
if (offsetDiagChanged) [self offsetDiag:@"kvo-cache-after" page:_currentIndex scroll:scrollView];
|
|
3510
|
+
#endif
|
|
3274
3511
|
[self updateHeaderForScrollView:scrollView];
|
|
3275
3512
|
return;
|
|
3276
3513
|
}
|
|
@@ -3279,6 +3516,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3279
3516
|
|
|
3280
3517
|
- (void)updateHeaderForScrollView:(UIScrollView *)scrollView
|
|
3281
3518
|
{
|
|
3519
|
+
if (_nativeSmoothHeaderScrollEnabled &&
|
|
3520
|
+
(_syntheticPageRestore || _transitionPageOffsets != nil)) return;
|
|
3282
3521
|
CGFloat logicalOffset = scrollView.contentOffset.y + scrollView.contentInset.top;
|
|
3283
3522
|
_currentLogicalOffset = logicalOffset;
|
|
3284
3523
|
_headerOffset = MIN(MAX(logicalOffset, 0), _headerHeight);
|
|
@@ -3322,6 +3561,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3322
3561
|
|
|
3323
3562
|
- (void)goTo:(NSInteger)index animated:(BOOL)animated
|
|
3324
3563
|
{
|
|
3564
|
+
[self offsetDiag:@"go-to" page:index scroll:_observedScrollView];
|
|
3325
3565
|
if (_isBeingRecycled || index < 0 || index >= _pageControllers.count) return;
|
|
3326
3566
|
_hasReceivedPageCommand = YES;
|
|
3327
3567
|
_hasAppliedInitialPage = YES;
|
|
@@ -3347,6 +3587,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3347
3587
|
return;
|
|
3348
3588
|
}
|
|
3349
3589
|
|
|
3590
|
+
[self beginPageStateTransition];
|
|
3350
3591
|
[self preparePageForHorizontalTransitionAtIndex:index];
|
|
3351
3592
|
_destinationIndex = index;
|
|
3352
3593
|
[self liftSharedHeadersForPagerTransition:@"programmatic"];
|
|
@@ -3366,6 +3607,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3366
3607
|
void (^animations)(void) = ^{
|
|
3367
3608
|
[self->_pagerScrollView setContentOffset:CGPointMake(targetOffset, 0)];
|
|
3368
3609
|
[self->_nativeTabBarView setProgress:index];
|
|
3610
|
+
[self updatePageStateTransitionProgress:index];
|
|
3369
3611
|
};
|
|
3370
3612
|
void (^completion)(BOOL) = ^(BOOL finished) {
|
|
3371
3613
|
__strong __typeof__(weakSelf) self = weakSelf;
|
|
@@ -3373,6 +3615,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3373
3615
|
self->_transitionId != capturedTransition || self->_isBeingRecycled) return;
|
|
3374
3616
|
self->_pagerScrollView.scrollEnabled = self->_scrollEnabled;
|
|
3375
3617
|
if (finished) {
|
|
3618
|
+
[self detachScrollObserver];
|
|
3376
3619
|
self->_currentIndex = index;
|
|
3377
3620
|
self->_destinationIndex = index;
|
|
3378
3621
|
[self->_nativeTabBarView setProgress:index];
|
|
@@ -3410,6 +3653,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3410
3653
|
if (self == nil || self->_generation != capturedGeneration ||
|
|
3411
3654
|
self->_transitionId != capturedTransition || self->_isBeingRecycled) return;
|
|
3412
3655
|
if (finished) {
|
|
3656
|
+
[self detachScrollObserver];
|
|
3413
3657
|
self->_currentIndex = index;
|
|
3414
3658
|
self->_destinationIndex = index;
|
|
3415
3659
|
[self->_nativeTabBarView setProgress:index];
|
|
@@ -3434,6 +3678,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3434
3678
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
3435
3679
|
if (self->_generation != generation || self->_transitionId != transition ||
|
|
3436
3680
|
self->_isBeingRecycled) return;
|
|
3681
|
+
[self finishPageStateTransition];
|
|
3437
3682
|
self->_transitioning = NO;
|
|
3438
3683
|
if (self->_hasPendingDirectNativePagerChange) {
|
|
3439
3684
|
BOOL enabled = self->_pendingDirectNativePagerEnabled;
|
|
@@ -3497,9 +3742,11 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3497
3742
|
|
|
3498
3743
|
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
|
|
3499
3744
|
{
|
|
3745
|
+
[self offsetDiag:@"pager-drag-begin" page:_currentIndex scroll:_observedScrollView];
|
|
3500
3746
|
if (_nativeSmoothHeaderScrollEnabled && scrollView == _pagerScrollView) {
|
|
3501
3747
|
[_contentPressCancellationGesture cancelPressForPan:scrollView.panGestureRecognizer];
|
|
3502
3748
|
}
|
|
3749
|
+
[self beginPageStateTransition];
|
|
3503
3750
|
if (_directNativePagerEnabled) {
|
|
3504
3751
|
[self preparePageForHorizontalTransitionAtIndex:_currentIndex - 1];
|
|
3505
3752
|
[self preparePageForHorizontalTransitionAtIndex:_currentIndex + 1];
|
|
@@ -3651,6 +3898,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3651
3898
|
NSInteger position = (NSInteger)floor(progress);
|
|
3652
3899
|
CGFloat offset = progress - position;
|
|
3653
3900
|
position = MAX(0, MIN(position, (NSInteger)_pageControllers.count - 1));
|
|
3901
|
+
[self updatePageStateTransitionProgress:progress];
|
|
3654
3902
|
[_nativeTabBarView setProgress:progress];
|
|
3655
3903
|
const auto emitter = [self eventEmitter];
|
|
3656
3904
|
if (emitter) {
|
|
@@ -3668,6 +3916,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3668
3916
|
CGFloat transitionProgress = RNCClamp(fabs(rawOffset), 0, 1);
|
|
3669
3917
|
tabProgress = _currentIndex + (_destinationIndex - _currentIndex) * transitionProgress;
|
|
3670
3918
|
}
|
|
3919
|
+
[self updatePageStateTransitionProgress:tabProgress];
|
|
3671
3920
|
[_nativeTabBarView setProgress:tabProgress];
|
|
3672
3921
|
const auto emitter = [self eventEmitter];
|
|
3673
3922
|
if (emitter) {
|
|
@@ -3685,6 +3934,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3685
3934
|
|
|
3686
3935
|
- (void)emitPageSelected:(NSInteger)position
|
|
3687
3936
|
{
|
|
3937
|
+
[self offsetDiag:@"selection-complete" page:position scroll:_observedScrollView];
|
|
3688
3938
|
const auto emitter = [self eventEmitter];
|
|
3689
3939
|
if (emitter) emitter->onPageSelected({.position = (double)position});
|
|
3690
3940
|
}
|
|
@@ -10,6 +10,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
10
10
|
|
|
11
11
|
// Native inset ownership stays separate from Fabric/Yoga content layout.
|
|
12
12
|
@protocol RNCNativeScrollerInsetCoordinator <NSObject>
|
|
13
|
+
- (void)nativeScrollerWillReceiveScrollCommand:(RNCNativeScrollerComponentView *)scroller;
|
|
13
14
|
- (void)nativeScroller:(RNCNativeScrollerComponentView *)scroller
|
|
14
15
|
callerInsetChangedFrom:(UIEdgeInsets)previous to:(UIEdgeInsets)next;
|
|
15
16
|
@end
|