@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.
@@ -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
- if (nativeSmoothHeaderScrollEnabled) {
921
- pendingPageRestores.add(key)
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
- pendingPageRestores.remove(key)
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
- if (nativeSmoothHeaderScrollEnabled) pendingPageRestores.add(key)
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
- pendingPageRestores.remove(key)
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
- if (nativeSmoothHeaderScrollEnabled) pendingPageRestores.add(key)
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
- pendingPageRestores.remove(key)
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.171",
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.171",
75
+ "@onekeyfe/react-native-native-logger": "3.0.172",
76
76
  "react": "*",
77
77
  "react-native": "*"
78
78
  },