@onekeyfe/react-native-pager-view 3.0.171 → 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 +72 -13
- package/docs/SPEC.md +12 -0
- package/ios/RNCCollapsiblePagerViewComponentView.mm +19 -0
- package/ios/RNCNativeScrollerComponentView.h +1 -0
- package/ios/RNCNativeScrollerComponentView.mm +8 -0
- package/ios/tests/RNCCollapsiblePagerFirstFrameTests.mm +84 -0
- package/package.json +2 -2
|
@@ -139,6 +139,34 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
139
139
|
private var syntheticPageRestore = false
|
|
140
140
|
private var resumeTransitionSource: Int? = null
|
|
141
141
|
private val pendingPageRestores = HashSet<String>()
|
|
142
|
+
private class PageRestoreRequest(view: View, val pageIndex: Int, val generation: Int) {
|
|
143
|
+
val view = WeakReference(view)
|
|
144
|
+
}
|
|
145
|
+
private val pageRestoreRequests = HashMap<String, PageRestoreRequest>()
|
|
146
|
+
|
|
147
|
+
private fun registerPageRestore(key: String, view: View, pageIndex: Int): PageRestoreRequest? {
|
|
148
|
+
if (!nativeSmoothHeaderScrollEnabled) return null
|
|
149
|
+
val previous = pageRestoreRequests[key]?.view?.get()
|
|
150
|
+
if (previous is NativeScrollerViewport && previous !== view) previous.host.cancelPendingScrollRestore()
|
|
151
|
+
ownedScrollerRestoreRequests.remove(key)
|
|
152
|
+
return PageRestoreRequest(view, pageIndex, attachmentGeneration).also {
|
|
153
|
+
pageRestoreRequests[key] = it
|
|
154
|
+
pendingPageRestores.add(key)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
private fun isCurrentPageRestore(key: String, request: PageRestoreRequest?): Boolean =
|
|
159
|
+
!nativeSmoothHeaderScrollEnabled || (request != null && pageRestoreRequests[key] === request &&
|
|
160
|
+
key in pendingPageRestores && request.generation == attachmentGeneration &&
|
|
161
|
+
pageKey(request.pageIndex) == key)
|
|
162
|
+
|
|
163
|
+
private fun clearPageRestoreForView(key: String, view: View, request: PageRestoreRequest? = null) {
|
|
164
|
+
if (request != null && pageRestoreRequests[key] !== request) return
|
|
165
|
+
if (pageRestoreRequests[key]?.view?.get() !== view) return
|
|
166
|
+
pageRestoreRequests.remove(key)
|
|
167
|
+
pendingPageRestores.remove(key)
|
|
168
|
+
ownedScrollerRestoreRequests.remove(key)
|
|
169
|
+
}
|
|
142
170
|
private var observedRecyclerView: RecyclerView? = null
|
|
143
171
|
private var observedRecyclerKey: String? = null
|
|
144
172
|
private var observedScrollListener: RecyclerView.OnScrollListener? = null
|
|
@@ -200,6 +228,18 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
200
228
|
var headerOffsetPx = 0
|
|
201
229
|
private set
|
|
202
230
|
var pageKeys: List<String> = emptyList()
|
|
231
|
+
set(value) {
|
|
232
|
+
if (field == value) return
|
|
233
|
+
pageRestoreRequests.entries.toList().forEach { (key, request) ->
|
|
234
|
+
if ((value.getOrNull(request.pageIndex) ?: "page-${request.pageIndex}") != key) {
|
|
235
|
+
(request.view.get() as? NativeScrollerViewport)?.host?.cancelPendingScrollRestore()
|
|
236
|
+
pageRestoreRequests.remove(key)
|
|
237
|
+
pendingPageRestores.remove(key)
|
|
238
|
+
ownedScrollerRestoreRequests.remove(key)
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
field = value
|
|
242
|
+
}
|
|
203
243
|
var retainedPages: String = "[]"
|
|
204
244
|
var onHeaderOffsetChanged: (() -> Unit)? = null
|
|
205
245
|
var nativeSmoothHeaderScrollEnabled = false
|
|
@@ -388,6 +428,9 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
388
428
|
fun removeAllReactChildren() {
|
|
389
429
|
releaseRefreshForeground()
|
|
390
430
|
detachPrimaryScrollObserver()
|
|
431
|
+
pageRestoreRequests.clear()
|
|
432
|
+
pendingPageRestores.clear()
|
|
433
|
+
ownedScrollerRestoreRequests.clear()
|
|
391
434
|
// Weak keys may disappear after size is read; Collection.toArray tolerates this.
|
|
392
435
|
for (recycler in originalRecyclerPadding.keys.toTypedArray()) {
|
|
393
436
|
restoreRecyclerInsets(recycler)
|
|
@@ -917,13 +960,11 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
917
960
|
val key = pageKey(pageIndex)
|
|
918
961
|
if (ownedScrollerKeys[scroller] == key) return
|
|
919
962
|
ownedScrollerKeys[scroller] = key
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
ownedScrollerRestoreRequests[key] = WeakReference(scroller) to null
|
|
923
|
-
}
|
|
963
|
+
val restoreRequest = registerPageRestore(key, scroller, pageIndex)
|
|
964
|
+
if (nativeSmoothHeaderScrollEnabled) ownedScrollerRestoreRequests[key] = WeakReference(scroller) to null
|
|
924
965
|
val generation = attachmentGeneration
|
|
925
966
|
postForCurrentAttachment {
|
|
926
|
-
if (ownedScrollerKeys[scroller] == key && generation == attachmentGeneration &&
|
|
967
|
+
if (isCurrentPageRestore(key, restoreRequest) && ownedScrollerKeys[scroller] == key && generation == attachmentGeneration &&
|
|
927
968
|
scroller.isAttachedToWindow && (!nativeSmoothHeaderScrollEnabled ||
|
|
928
969
|
(key in pendingPageRestores && ownedScrollerRestoreRequests[key]?.first?.get() === scroller))) {
|
|
929
970
|
offsetDiag("owned-restore-before", scroller, key)
|
|
@@ -942,6 +983,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
942
983
|
scroller.stopMotion()
|
|
943
984
|
scroller.host.cancelPendingScrollRestore()
|
|
944
985
|
ownedScrollerKeys.remove(scroller)?.let { key ->
|
|
986
|
+
clearPageRestoreForView(key, scroller)
|
|
945
987
|
if (ownedScrollerRestoreRequests[key]?.first?.get() === scroller) ownedScrollerRestoreRequests.remove(key)
|
|
946
988
|
}
|
|
947
989
|
publishConsumedOffset(scroller, remove = true)
|
|
@@ -982,8 +1024,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
982
1024
|
val request = ownedScrollerRestoreRequests[key]
|
|
983
1025
|
if (request == null || request.first.get() !== scroller || request.second != attachmentGeneration ||
|
|
984
1026
|
scroller.width <= 0 || scroller.height <= 0 || scroller.host.hasPendingScrollRestore()) return
|
|
985
|
-
|
|
986
|
-
ownedScrollerRestoreRequests.remove(key)
|
|
1027
|
+
clearPageRestoreForView(key, scroller)
|
|
987
1028
|
}
|
|
988
1029
|
if (observedOwnedScroller !== scroller || observedOwnedScrollerKey != key ||
|
|
989
1030
|
key != currentPageKey() || transitionHeaderOffsets != null || syntheticPageRestore) return
|
|
@@ -1059,13 +1100,18 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
1059
1100
|
val key = pageKey(pageIndex)
|
|
1060
1101
|
if (restoredRecyclerKeys[recycler] != key) {
|
|
1061
1102
|
restoredRecyclerKeys[recycler] = key
|
|
1062
|
-
|
|
1103
|
+
val restoreRequest = registerPageRestore(key, recycler, pageIndex)
|
|
1063
1104
|
val generation = attachmentGeneration
|
|
1064
1105
|
recycler.post {
|
|
1106
|
+
if (!isCurrentPageRestore(key, restoreRequest)) {
|
|
1107
|
+
offsetDiag("post-skip-cancelled-page=$pageIndex", recycler, key)
|
|
1108
|
+
return@post
|
|
1109
|
+
}
|
|
1065
1110
|
if (!isAttachedToWindow || generation != attachmentGeneration ||
|
|
1066
1111
|
restoredRecyclerKeys[recycler] != key ||
|
|
1067
1112
|
originalRecyclerPadding[recycler] !== original) {
|
|
1068
1113
|
if (restoredRecyclerKeys[recycler] == key) restoredRecyclerKeys.remove(recycler)
|
|
1114
|
+
clearPageRestoreForView(key, recycler, restoreRequest)
|
|
1069
1115
|
return@post
|
|
1070
1116
|
}
|
|
1071
1117
|
val saved = transitionPageOffsets?.get(key) ?: pageOffsets[key] ?: 0
|
|
@@ -1073,10 +1119,12 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
1073
1119
|
offsetDiag("recycler-restore-before-saved=$saved", recycler, key)
|
|
1074
1120
|
if (nativeSmoothHeaderScrollEnabled && (recycler.height <= 0 || recycler.childCount == 0)) {
|
|
1075
1121
|
restoredRecyclerKeys.remove(recycler)
|
|
1122
|
+
if (pageRestoreRequests[key] === restoreRequest) pageRestoreRequests.remove(key)
|
|
1076
1123
|
return@post
|
|
1077
1124
|
}
|
|
1078
1125
|
if (saved != current) recycler.scrollBy(0, saved - current)
|
|
1079
|
-
|
|
1126
|
+
if (nativeSmoothHeaderScrollEnabled) clearPageRestoreForView(key, recycler, restoreRequest)
|
|
1127
|
+
else pendingPageRestores.remove(key)
|
|
1080
1128
|
offsetDiag("recycler-restore-after", recycler, key)
|
|
1081
1129
|
}
|
|
1082
1130
|
}
|
|
@@ -1123,13 +1171,18 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
1123
1171
|
val key = pageKey(pageIndex)
|
|
1124
1172
|
if (restoredScrollerKeys[scroller] != key) {
|
|
1125
1173
|
restoredScrollerKeys[scroller] = key
|
|
1126
|
-
|
|
1174
|
+
val restoreRequest = registerPageRestore(key, scroller, pageIndex)
|
|
1127
1175
|
val generation = attachmentGeneration
|
|
1128
1176
|
scroller.post {
|
|
1177
|
+
if (!isCurrentPageRestore(key, restoreRequest)) {
|
|
1178
|
+
offsetDiag("post-skip-cancelled-page=$pageIndex", scroller, key)
|
|
1179
|
+
return@post
|
|
1180
|
+
}
|
|
1129
1181
|
if (!isAttachedToWindow || generation != attachmentGeneration ||
|
|
1130
1182
|
restoredScrollerKeys[scroller] != key ||
|
|
1131
1183
|
originalScrollerPadding[scroller] !== original) {
|
|
1132
1184
|
if (restoredScrollerKeys[scroller] == key) restoredScrollerKeys.remove(scroller)
|
|
1185
|
+
clearPageRestoreForView(key, scroller, restoreRequest)
|
|
1133
1186
|
return@post
|
|
1134
1187
|
}
|
|
1135
1188
|
val saved = transitionPageOffsets?.get(key) ?: pageOffsets[key] ?: 0
|
|
@@ -1137,10 +1190,12 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
1137
1190
|
if (nativeSmoothHeaderScrollEnabled &&
|
|
1138
1191
|
(scroller.height <= 0 || (scroller.getChildAt(0)?.height ?: 0) <= 0)) {
|
|
1139
1192
|
restoredScrollerKeys.remove(scroller)
|
|
1193
|
+
if (pageRestoreRequests[key] === restoreRequest) pageRestoreRequests.remove(key)
|
|
1140
1194
|
return@post
|
|
1141
1195
|
}
|
|
1142
1196
|
if (scroller.scrollY != saved) scroller.scrollTo(scroller.scrollX, saved)
|
|
1143
|
-
|
|
1197
|
+
if (nativeSmoothHeaderScrollEnabled) clearPageRestoreForView(key, scroller, restoreRequest)
|
|
1198
|
+
else pendingPageRestores.remove(key)
|
|
1144
1199
|
offsetDiag("scrollview-restore-after", scroller, key)
|
|
1145
1200
|
}
|
|
1146
1201
|
}
|
|
@@ -1200,7 +1255,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
1200
1255
|
publishConsumedOffset(recycler, remove = true)
|
|
1201
1256
|
val original = originalRecyclerPadding.remove(recycler) ?: return
|
|
1202
1257
|
updateRecyclerPaddingOwnership(recycler, original)
|
|
1203
|
-
restoredRecyclerKeys.remove(recycler)
|
|
1258
|
+
restoredRecyclerKeys.remove(recycler)?.let { clearPageRestoreForView(it, recycler) }
|
|
1204
1259
|
recycler.clipToPadding = original.clipToPadding
|
|
1205
1260
|
recycler.setPadding(original.left, original.top, original.right, original.bottom)
|
|
1206
1261
|
}
|
|
@@ -1208,7 +1263,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
1208
1263
|
private fun restoreNativeScrollerInsets(scroller: ScrollView) {
|
|
1209
1264
|
val original = originalScrollerPadding.remove(scroller) ?: return
|
|
1210
1265
|
updateScrollerPaddingOwnership(scroller, original)
|
|
1211
|
-
restoredScrollerKeys.remove(scroller)
|
|
1266
|
+
restoredScrollerKeys.remove(scroller)?.let { clearPageRestoreForView(it, scroller) }
|
|
1212
1267
|
if (scroller is ReactScrollView) {
|
|
1213
1268
|
val state = scroller.reactScrollViewScrollState
|
|
1214
1269
|
scroller.setScrollAwayPaddingEnabledUnstable(
|
|
@@ -1583,6 +1638,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
1583
1638
|
if (type == ViewCompat.TYPE_TOUCH) pendingRefreshPull = null
|
|
1584
1639
|
nestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes, type)
|
|
1585
1640
|
if (type == ViewCompat.TYPE_TOUCH) {
|
|
1641
|
+
pageRestoreRequests.remove(currentPageKey())
|
|
1586
1642
|
pendingPageRestores.remove(currentPageKey())
|
|
1587
1643
|
ownedScrollerRestoreRequests.remove(currentPageKey())
|
|
1588
1644
|
}
|
|
@@ -1705,6 +1761,9 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
1705
1761
|
nestedNativeGestureStarted = false
|
|
1706
1762
|
viewTreeObserver.removeOnPreDrawListener(pageContentLayoutListener)
|
|
1707
1763
|
detachPrimaryScrollObserver()
|
|
1764
|
+
pageRestoreRequests.clear()
|
|
1765
|
+
pendingPageRestores.clear()
|
|
1766
|
+
ownedScrollerRestoreRequests.clear()
|
|
1708
1767
|
transitionHeaderOffsets?.let { headers ->
|
|
1709
1768
|
resumeTransitionSource = transitionSourcePage
|
|
1710
1769
|
syntheticPageRestore = true
|
package/docs/SPEC.md
CHANGED
|
@@ -39,6 +39,18 @@ body offset. Existing pre-draw observation also reconciles a same-position comma
|
|
|
39
39
|
that emits no scroll notification. Zero-sized geometry retains the pending intent;
|
|
40
40
|
no timeout infers that delayed content is permanently short.
|
|
41
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
|
+
|
|
42
54
|
## Independent NativeScroller migration
|
|
43
55
|
|
|
44
56
|
Status: implemented in source. Both native builds pass. Android focused emulator
|
|
@@ -3398,6 +3398,25 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3398
3398
|
}
|
|
3399
3399
|
}
|
|
3400
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
|
+
|
|
3401
3420
|
- (void)nativeScroller:(RNCNativeScrollerComponentView *)scroller
|
|
3402
3421
|
callerInsetChangedFrom:(UIEdgeInsets)previous to:(UIEdgeInsets)next
|
|
3403
3422
|
{
|
|
@@ -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
|
|
@@ -428,6 +428,14 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
428
428
|
- (void)scrollTo:(double)x y:(double)y animated:(BOOL)animated
|
|
429
429
|
{
|
|
430
430
|
if (_recycled || !isfinite(x) || !isfinite(y)) return;
|
|
431
|
+
for (UIView *owner = self.superview; owner != nil; owner = owner.superview) {
|
|
432
|
+
if ([owner conformsToProtocol:@protocol(RNCNativeScrollerInsetCoordinator)]) {
|
|
433
|
+
[(id<RNCNativeScrollerInsetCoordinator>)owner nativeScrollerWillReceiveScrollCommand:self];
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
#if DEBUG
|
|
437
|
+
[self offsetDiag:[NSString stringWithFormat:@"command-scrollTo-enter-x=%.4f-y=%.4f-animated=%d", x, y, animated]];
|
|
438
|
+
#endif
|
|
431
439
|
[self stopScrolling];
|
|
432
440
|
UIEdgeInsets inset = _scrollView.adjustedContentInset;
|
|
433
441
|
CGPoint target = CGPointMake(MAX(-inset.left, MIN(x,
|
|
@@ -22,11 +22,95 @@ using namespace facebook::react;
|
|
|
22
22
|
- (void)applyInsetsToScrollView:(UIScrollView *)scrollView pageIndex:(NSInteger)index restore:(BOOL)restore;
|
|
23
23
|
@end
|
|
24
24
|
|
|
25
|
+
@interface RNCNativeScrollerComponentView (ScrollCommandTesting)
|
|
26
|
+
- (void)scrollTo:(double)x y:(double)y animated:(BOOL)animated;
|
|
27
|
+
- (void)scrollToEnd:(BOOL)animated;
|
|
28
|
+
@end
|
|
29
|
+
|
|
25
30
|
@interface RNCCollapsiblePagerFirstFrameTests : XCTestCase
|
|
26
31
|
@end
|
|
27
32
|
|
|
28
33
|
@implementation RNCCollapsiblePagerFirstFrameTests
|
|
29
34
|
|
|
35
|
+
- (void)testReachableTopCommandBeforeLayoutMustNotReplayPendingPagePosition
|
|
36
|
+
{
|
|
37
|
+
for (NSNumber *command in @[@0, @1, @2]) {
|
|
38
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:1];
|
|
39
|
+
RNCNativeScrollerComponentView *scroller = [self addScrollerToPager:pager atIndex:0];
|
|
40
|
+
NSMutableDictionary *offsets = [pager valueForKey:@"pageOffsets"];
|
|
41
|
+
offsets[@"page-0"] = @660;
|
|
42
|
+
scroller.scrollView.contentSize = CGSizeMake(0, 0);
|
|
43
|
+
[pager attachScrollObserverForCurrentPage];
|
|
44
|
+
XCTAssertTrue([[pager valueForKey:@"pendingPageRestoreKeys"] containsObject:@"page-0"]);
|
|
45
|
+
XCTAssertEqualObjects(offsets[@"page-0"], @660);
|
|
46
|
+
XCTAssertEqual([pager valueForKey:@"observedScrollView"], scroller.scrollView);
|
|
47
|
+
XCTAssertEqualObjects([pager valueForKey:@"observedPageKey"], @"page-0");
|
|
48
|
+
XCTAssertEqualObjects([pager valueForKey:@"currentIndex"], @0);
|
|
49
|
+
if (command.boolValue) {
|
|
50
|
+
if (command.integerValue == 2) {
|
|
51
|
+
CGFloat reachableEndQ = MAX(-scroller.scrollView.adjustedContentInset.top,
|
|
52
|
+
scroller.scrollView.contentSize.height - scroller.scrollView.bounds.size.height +
|
|
53
|
+
scroller.scrollView.adjustedContentInset.bottom) + scroller.scrollView.contentInset.top;
|
|
54
|
+
XCTAssertEqualWithAccuracy(reachableEndQ, 120, 0.01);
|
|
55
|
+
XCTAssertEqualWithAccuracy(scroller.scrollView.contentOffset.y + scroller.scrollView.contentInset.top, reachableEndQ, 0.01);
|
|
56
|
+
[scroller scrollToEnd:NO];
|
|
57
|
+
}
|
|
58
|
+
else [scroller scrollTo:0 y:-scroller.scrollView.adjustedContentInset.top animated:NO];
|
|
59
|
+
XCTAssertEqualWithAccuracy(scroller.scrollView.contentOffset.y + scroller.scrollView.contentInset.top, command.integerValue == 2 ? 120 : 0, 0.01);
|
|
60
|
+
XCTAssertFalse([[pager valueForKey:@"pendingPageRestoreKeys"] containsObject:@"page-0"]);
|
|
61
|
+
XCTAssertEqualWithAccuracy([offsets[@"page-0"] doubleValue], command.integerValue == 2 ? 120 : 0, 0.01);
|
|
62
|
+
XCTAssertEqual([pager valueForKey:@"observedScrollView"], scroller.scrollView);
|
|
63
|
+
XCTAssertEqualObjects([pager valueForKey:@"observedPageKey"], @"page-0");
|
|
64
|
+
}
|
|
65
|
+
// The existing observed content-size path retries the pending restoration.
|
|
66
|
+
scroller.scrollView.contentSize = CGSizeMake(320, 1500);
|
|
67
|
+
XCTAssertFalse([[pager valueForKey:@"pendingPageRestoreKeys"] containsObject:@"page-0"]);
|
|
68
|
+
CGFloat expected = command.integerValue == 2 ? 120 : (command.boolValue ? 0 : 660);
|
|
69
|
+
XCTAssertEqualWithAccuracy(scroller.scrollView.contentOffset.y + scroller.scrollView.contentInset.top,
|
|
70
|
+
expected, 0.01, @"command=%@ must not replay a cancelled reading position", command);
|
|
71
|
+
XCTAssertEqualWithAccuracy([[pager valueForKey:@"headerOffset"] doubleValue], command.integerValue == 1 ? 0 : 120, 0.01);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
- (void)testScrollCommandCancellationRespectsOwnerAndValidity
|
|
76
|
+
{
|
|
77
|
+
for (NSNumber *mode in @[@0, @1, @2]) {
|
|
78
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:2];
|
|
79
|
+
RNCNativeScrollerComponentView *current = [self addScrollerToPager:pager atIndex:0];
|
|
80
|
+
RNCNativeScrollerComponentView *other = [self addScrollerToPager:pager atIndex:1];
|
|
81
|
+
NSMutableDictionary *offsets = [pager valueForKey:@"pageOffsets"];
|
|
82
|
+
offsets[@"page-0"] = @660;
|
|
83
|
+
offsets[@"page-1"] = @360;
|
|
84
|
+
current.scrollView.contentSize = CGSizeMake(0, 0);
|
|
85
|
+
[pager attachScrollObserverForCurrentPage];
|
|
86
|
+
XCTAssertTrue([[pager valueForKey:@"pendingPageRestoreKeys"] containsObject:@"page-0"]);
|
|
87
|
+
if (mode.integerValue == 0) {
|
|
88
|
+
[current scrollTo:0 y:NAN animated:NO];
|
|
89
|
+
} else if (mode.integerValue == 1) {
|
|
90
|
+
[other scrollTo:0 y:-other.scrollView.adjustedContentInset.top animated:NO];
|
|
91
|
+
} else {
|
|
92
|
+
NSUInteger generation = [[pager valueForKey:@"generation"] unsignedIntegerValue];
|
|
93
|
+
[pager setValue:@(generation + 1) forKey:@"observedPageGeneration"];
|
|
94
|
+
[current scrollTo:0 y:-current.scrollView.adjustedContentInset.top animated:NO];
|
|
95
|
+
}
|
|
96
|
+
XCTAssertTrue([[pager valueForKey:@"pendingPageRestoreKeys"] containsObject:@"page-0"]);
|
|
97
|
+
XCTAssertEqualObjects(offsets[@"page-0"], @660);
|
|
98
|
+
XCTAssertEqualObjects(offsets[@"page-1"], @360);
|
|
99
|
+
}
|
|
100
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:2];
|
|
101
|
+
RNCNativeScrollerComponentView *current = [self addScrollerToPager:pager atIndex:0];
|
|
102
|
+
[self addScrollerToPager:pager atIndex:1];
|
|
103
|
+
NSMutableDictionary *offsets = [pager valueForKey:@"pageOffsets"];
|
|
104
|
+
offsets[@"page-0"] = @0;
|
|
105
|
+
offsets[@"page-1"] = @360;
|
|
106
|
+
[pager attachScrollObserverForCurrentPage];
|
|
107
|
+
XCTAssertFalse([[pager valueForKey:@"pendingPageRestoreKeys"] containsObject:@"page-0"]);
|
|
108
|
+
[current scrollTo:0 y:200 - current.scrollView.contentInset.top animated:NO];
|
|
109
|
+
XCTAssertEqualWithAccuracy(current.scrollView.contentOffset.y + current.scrollView.contentInset.top, 200, 0.01);
|
|
110
|
+
XCTAssertEqualWithAccuracy([offsets[@"page-0"] doubleValue], 200, 0.01);
|
|
111
|
+
XCTAssertEqualObjects(offsets[@"page-1"], @360);
|
|
112
|
+
}
|
|
113
|
+
|
|
30
114
|
- (void)testTransitionLayoutCannotSaveSourceOffsetIntoDestinationCache
|
|
31
115
|
{
|
|
32
116
|
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:2];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/react-native-pager-view",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.172",
|
|
4
4
|
"description": "React Native wrapper for Android and iOS ViewPager",
|
|
5
5
|
"source": "./src/index.tsx",
|
|
6
6
|
"main": "./lib/module/index.js",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"typescript": "^5.9.2"
|
|
73
73
|
},
|
|
74
74
|
"peerDependencies": {
|
|
75
|
-
"@onekeyfe/react-native-native-logger": "3.0.
|
|
75
|
+
"@onekeyfe/react-native-native-logger": "3.0.172",
|
|
76
76
|
"react": "*",
|
|
77
77
|
"react-native": "*"
|
|
78
78
|
},
|