@onekeyfe/react-native-pager-view 3.0.179-alpha.2026101503 → 3.0.179-alpha.302
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 +0 -10
- package/android/src/main/java/com/reactnativepagerview/NativeScrollerViewManager.kt +0 -4
- package/docs/SPEC.md +1 -77
- package/ios/RNCCollapsiblePagerViewComponentView.mm +138 -838
- package/ios/RNCNativeScrollerComponentView.mm +38 -93
- package/ios/RNCPagerViewComponentView.h +0 -7
- package/ios/RNCPagerViewComponentView.mm +0 -34
- package/ios/tests/RNCCollapsiblePagerHitTestTests.mm +0 -49
- package/lib/module/CollapsiblePagerView.js +1 -27
- package/lib/module/CollapsiblePagerView.web.js +0 -4
- package/lib/module/CollapsiblePagerViewNativeComponent.ts +0 -6
- package/lib/module/NativeScroller.js +82 -105
- package/lib/module/NativeScroller.web.js +0 -2
- package/lib/module/NativeScrollerNativeComponent.ts +0 -2
- package/lib/typescript/src/CollapsiblePagerView.d.ts +1 -10
- package/lib/typescript/src/CollapsiblePagerView.web.d.ts +0 -4
- package/lib/typescript/src/CollapsiblePagerViewNativeComponent.d.ts +0 -8
- package/lib/typescript/src/NativeScroller.d.ts +0 -4
- package/lib/typescript/src/NativeScrollerNativeComponent.d.ts +0 -2
- package/package.json +2 -2
- package/src/CollapsiblePagerView.tsx +0 -44
- package/src/CollapsiblePagerView.web.tsx +0 -8
- package/src/CollapsiblePagerViewNativeComponent.ts +0 -6
- package/src/NativeScroller.tsx +2 -27
- package/src/NativeScroller.web.tsx +0 -2
- package/src/NativeScrollerNativeComponent.ts +0 -2
|
@@ -33,15 +33,12 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
33
33
|
@end
|
|
34
34
|
|
|
35
35
|
@implementation RNCNativeScrollerComponentView {
|
|
36
|
+
#if DEBUG
|
|
37
|
+
NSUInteger _offsetDiagSequence;
|
|
38
|
+
#endif
|
|
36
39
|
RNCNativeScrollerShadowNode::ConcreteState::Shared _state;
|
|
37
40
|
RNCNativeVerticalScrollView *_scrollView;
|
|
38
41
|
UIView<RCTComponentViewProtocol> *_reactContent;
|
|
39
|
-
__weak UIView *_topOverlay;
|
|
40
|
-
CGFloat _appliedOverlayInset;
|
|
41
|
-
UIScrollEdgeElementContainerInteraction *_topEdgeInteraction;
|
|
42
|
-
__weak UIView *_topEdgeInteractionHost;
|
|
43
|
-
UILabel *_topEdgeMarker;
|
|
44
|
-
UIScrollEdgeEffectStyle *_originalTopEdgeStyle;
|
|
45
42
|
UIRefreshControl *_refreshControl;
|
|
46
43
|
UIEdgeInsets _callerInset;
|
|
47
44
|
BOOL _dragActive;
|
|
@@ -57,6 +54,24 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
57
54
|
NSNumber *_lastContentViewportHeight;
|
|
58
55
|
}
|
|
59
56
|
|
|
57
|
+
- (void)offsetDiag:(NSString *)reason
|
|
58
|
+
{
|
|
59
|
+
#if DEBUG
|
|
60
|
+
Class logClass = NSClassFromString(@"ReactNativeNativeLogger.OneKeyLog") ?: NSClassFromString(@"OneKeyLog");
|
|
61
|
+
SEL selector = NSSelectorFromString(@"debug::");
|
|
62
|
+
if (logClass == nil || ![logClass respondsToSelector:selector]) return;
|
|
63
|
+
NSString *message = [NSString stringWithFormat:
|
|
64
|
+
@"[HOME-OFFSET-DIAG] scroller phase=%@ seq=%lu wallMs=%.0f mono=%.6f host=%p tag=%ld scroll=%p generation=%lu physicalY=%.4f insetTop=%.4f logicalY=%.4f contentHeight=%.4f boundsHeight=%.4f adjustedBottom=%.4f needsClamp=%d drag=%d decel=%d refresh=%d",
|
|
65
|
+
reason, (unsigned long)++_offsetDiagSequence, NSDate.date.timeIntervalSince1970 * 1000, CACurrentMediaTime(),
|
|
66
|
+
(__bridge void *)self, (long)self.tag, (__bridge void *)_scrollView, (unsigned long)_generation,
|
|
67
|
+
_scrollView.contentOffset.y, _scrollView.contentInset.top, _scrollView.contentOffset.y + _scrollView.contentInset.top,
|
|
68
|
+
_scrollView.contentSize.height, _scrollView.bounds.size.height, _scrollView.adjustedContentInset.bottom,
|
|
69
|
+
_needsContentClamp, _scrollView.dragging, _scrollView.decelerating, _refreshControl.refreshing];
|
|
70
|
+
typedef void (*LogFunction)(id, SEL, NSString *, NSString *);
|
|
71
|
+
((LogFunction)[logClass methodForSelector:selector])(logClass, selector, @"NativeScroller", message);
|
|
72
|
+
#endif
|
|
73
|
+
}
|
|
74
|
+
|
|
60
75
|
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
|
61
76
|
{
|
|
62
77
|
return concreteComponentDescriptorProvider<RNCNativeScrollerComponentDescriptor>();
|
|
@@ -185,7 +200,9 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
185
200
|
_needsContentClamp |= size.height < _scrollView.contentSize.height;
|
|
186
201
|
CGPoint offset = _scrollView.contentOffset;
|
|
187
202
|
BOOL wasPulling = offset.y < -_scrollView.adjustedContentInset.top;
|
|
203
|
+
[self offsetDiag:@"content-size-before"];
|
|
188
204
|
_scrollView.contentSize = size;
|
|
205
|
+
[self offsetDiag:@"content-size-after"];
|
|
189
206
|
// UIKit can synchronously discard a pull when content becomes empty.
|
|
190
207
|
// Upper-range shrink correction remains owned by the deferred clamp.
|
|
191
208
|
if (wasPulling && _scrollView.contentOffset.y != offset.y) {
|
|
@@ -195,99 +212,20 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
195
212
|
}
|
|
196
213
|
BOOL refreshEnded = previous.refreshing && !next.refreshing;
|
|
197
214
|
[super updateProps:props oldProps:oldProps];
|
|
198
|
-
[self updateTopScrollEdge];
|
|
199
|
-
if (next.topScrollEdgeHard && _topOverlay == nil) {
|
|
200
|
-
__weak __typeof__(self) weakSelf = self;
|
|
201
|
-
dispatch_async(dispatch_get_main_queue(), ^{ [weakSelf updateTopScrollEdge]; });
|
|
202
|
-
}
|
|
203
215
|
if (refreshEnded) [self scheduleContentClamp];
|
|
204
216
|
[self updateFabricContentOrigin];
|
|
205
217
|
[self emitContentViewportChangeIfNeeded];
|
|
206
218
|
}
|
|
207
219
|
|
|
208
|
-
- (void)updateTopScrollEdge
|
|
209
|
-
{
|
|
210
|
-
if (@available(iOS 26.0, *)) {
|
|
211
|
-
const auto &props = *std::static_pointer_cast<const RNCNativeScrollerProps>(_props);
|
|
212
|
-
UIView *overlay = nil;
|
|
213
|
-
if (props.topScrollEdgeHard && !_recycled) {
|
|
214
|
-
NSUInteger fabricSiblings = 0;
|
|
215
|
-
for (UIView *candidate in self.superview.subviews) {
|
|
216
|
-
if (![candidate conformsToProtocol:@protocol(RCTComponentViewProtocol)]) continue;
|
|
217
|
-
fabricSiblings++;
|
|
218
|
-
if (candidate != self) overlay = candidate;
|
|
219
|
-
}
|
|
220
|
-
if (fabricSiblings != 2) overlay = nil;
|
|
221
|
-
}
|
|
222
|
-
_topOverlay = overlay;
|
|
223
|
-
CGFloat requestedOverlayInset = props.topScrollEdgeHard && !_recycled && overlay != nil &&
|
|
224
|
-
isfinite(props.topOverlayHeight) ? MAX(0, props.topOverlayHeight) : 0;
|
|
225
|
-
if (_appliedOverlayInset != requestedOverlayInset) {
|
|
226
|
-
// Keep UIKit's refresh rebound animation in control while it is overscrolled.
|
|
227
|
-
BOOL wasReturningFromPull = _scrollView.contentOffset.y < -_scrollView.adjustedContentInset.top - 0.5;
|
|
228
|
-
CGFloat logicalOffset = _scrollView.contentOffset.y + _scrollView.contentInset.top;
|
|
229
|
-
UIEdgeInsets current = _scrollView.contentInset;
|
|
230
|
-
current.top += requestedOverlayInset - _appliedOverlayInset;
|
|
231
|
-
_scrollView.contentInset = current;
|
|
232
|
-
_appliedOverlayInset = requestedOverlayInset;
|
|
233
|
-
if (!wasReturningFromPull) {
|
|
234
|
-
[_scrollView setContentOffset:CGPointMake(_scrollView.contentOffset.x,
|
|
235
|
-
logicalOffset - current.top) animated:NO];
|
|
236
|
-
}
|
|
237
|
-
[self emitContentViewportChangeIfNeeded];
|
|
238
|
-
}
|
|
239
|
-
BOOL enabled = props.topScrollEdgeHard && !_recycled && overlay != nil && self.window != nil;
|
|
240
|
-
if (!enabled || _topEdgeInteractionHost != overlay) {
|
|
241
|
-
if (_topEdgeInteraction != nil) {
|
|
242
|
-
[_topEdgeInteractionHost removeInteraction:_topEdgeInteraction];
|
|
243
|
-
_topEdgeInteraction.scrollView = nil;
|
|
244
|
-
_topEdgeInteraction = nil;
|
|
245
|
-
_topEdgeInteractionHost = nil;
|
|
246
|
-
}
|
|
247
|
-
[_topEdgeMarker removeFromSuperview];
|
|
248
|
-
_topEdgeMarker = nil;
|
|
249
|
-
if (_originalTopEdgeStyle != nil) {
|
|
250
|
-
_scrollView.topEdgeEffect.style = _originalTopEdgeStyle;
|
|
251
|
-
_originalTopEdgeStyle = nil;
|
|
252
|
-
}
|
|
253
|
-
if (!enabled) return;
|
|
254
|
-
}
|
|
255
|
-
if (_topEdgeInteraction == nil) {
|
|
256
|
-
_originalTopEdgeStyle = _scrollView.topEdgeEffect.style;
|
|
257
|
-
// UIKit needs a native element to shape the edge effect around React-drawn content.
|
|
258
|
-
_topEdgeMarker = [UILabel new];
|
|
259
|
-
_topEdgeMarker.frame = overlay.bounds;
|
|
260
|
-
_topEdgeMarker.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
261
|
-
_topEdgeMarker.text = @" ";
|
|
262
|
-
_topEdgeMarker.textColor = UIColor.clearColor;
|
|
263
|
-
_topEdgeMarker.backgroundColor = UIColor.clearColor;
|
|
264
|
-
_topEdgeMarker.userInteractionEnabled = NO;
|
|
265
|
-
_topEdgeMarker.isAccessibilityElement = NO;
|
|
266
|
-
_topEdgeMarker.accessibilityElementsHidden = YES;
|
|
267
|
-
[overlay addSubview:_topEdgeMarker];
|
|
268
|
-
_topEdgeInteraction = [UIScrollEdgeElementContainerInteraction new];
|
|
269
|
-
_topEdgeInteraction.edge = UIRectEdgeTop;
|
|
270
|
-
_topEdgeInteraction.scrollView = _scrollView;
|
|
271
|
-
[overlay addInteraction:_topEdgeInteraction];
|
|
272
|
-
_topEdgeInteractionHost = overlay;
|
|
273
|
-
}
|
|
274
|
-
_scrollView.topEdgeEffect.style = UIScrollEdgeEffectStyle.hardStyle;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
- (void)didMoveToSuperview
|
|
279
|
-
{
|
|
280
|
-
[super didMoveToSuperview];
|
|
281
|
-
[self updateTopScrollEdge];
|
|
282
|
-
}
|
|
283
|
-
|
|
284
220
|
- (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics
|
|
285
221
|
oldLayoutMetrics:(const LayoutMetrics &)oldLayoutMetrics
|
|
286
222
|
{
|
|
223
|
+
[self offsetDiag:@"layout-before"];
|
|
287
224
|
CGSize previous = _scrollView.bounds.size;
|
|
288
225
|
[super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
|
|
289
226
|
[self updateFabricContentOrigin];
|
|
290
227
|
[self emitContentViewportChangeIfNeeded];
|
|
228
|
+
[self offsetDiag:@"layout-after"];
|
|
291
229
|
if (!CGSizeEqualToSize(previous, _scrollView.bounds.size)) {
|
|
292
230
|
_needsContentClamp = YES;
|
|
293
231
|
[self scheduleContentClamp];
|
|
@@ -298,8 +236,7 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
298
236
|
{
|
|
299
237
|
CGFloat stickyHeight = 0;
|
|
300
238
|
for (NSNumber *height in _pagerStickyHeights.objectEnumerator) stickyHeight += height.doubleValue;
|
|
301
|
-
return MAX(0, _scrollView.bounds.size.height - _callerInset.top - _callerInset.bottom
|
|
302
|
-
- _appliedOverlayInset - stickyHeight);
|
|
239
|
+
return MAX(0, _scrollView.bounds.size.height - _callerInset.top - _callerInset.bottom - stickyHeight);
|
|
303
240
|
}
|
|
304
241
|
|
|
305
242
|
- (void)setPagerStickyHeight:(CGFloat)height forOwner:(id)owner
|
|
@@ -351,6 +288,7 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
351
288
|
- (void)scheduleContentClamp
|
|
352
289
|
{
|
|
353
290
|
if (!_needsContentClamp) return;
|
|
291
|
+
[self offsetDiag:@"clamp-scheduled"];
|
|
354
292
|
NSUInteger generation = _generation;
|
|
355
293
|
__weak __typeof__(self) weakSelf = self;
|
|
356
294
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
@@ -362,13 +300,19 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
362
300
|
|
|
363
301
|
- (void)clampContentOffsetIfNeeded
|
|
364
302
|
{
|
|
303
|
+
[self offsetDiag:@"clamp-enter-or-deferred"];
|
|
365
304
|
if (!_needsContentClamp || _scrollView.dragging || _scrollView.decelerating ||
|
|
366
305
|
_refreshControl.refreshing || _scrollView.bounds.size.height <= 0) return;
|
|
367
306
|
_needsContentClamp = NO;
|
|
368
307
|
CGFloat maximum = MAX(-_scrollView.adjustedContentInset.top,
|
|
369
308
|
_scrollView.contentSize.height - _scrollView.bounds.size.height + _scrollView.adjustedContentInset.bottom);
|
|
309
|
+
#if DEBUG
|
|
310
|
+
[self offsetDiag:[NSString stringWithFormat:@"clamp-candidate-max=%.4f", maximum]];
|
|
311
|
+
#endif
|
|
370
312
|
if (_scrollView.contentOffset.y > maximum) {
|
|
313
|
+
[self offsetDiag:@"clamp-applied-before"];
|
|
371
314
|
[_scrollView setContentOffset:CGPointMake(_scrollView.contentOffset.x, maximum) animated:NO];
|
|
315
|
+
[self offsetDiag:@"clamp-applied-after"];
|
|
372
316
|
}
|
|
373
317
|
}
|
|
374
318
|
|
|
@@ -409,6 +353,7 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
409
353
|
- (void)stopScrolling
|
|
410
354
|
{
|
|
411
355
|
if (_stopping) return;
|
|
356
|
+
[self offsetDiag:@"stop-before"];
|
|
412
357
|
_stopping = YES;
|
|
413
358
|
[_scrollView setContentOffset:_scrollView.contentOffset animated:NO];
|
|
414
359
|
if (_dragActive) {
|
|
@@ -420,6 +365,7 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
420
365
|
}
|
|
421
366
|
[self finishMomentum];
|
|
422
367
|
_stopping = NO;
|
|
368
|
+
[self offsetDiag:@"stop-after"];
|
|
423
369
|
}
|
|
424
370
|
|
|
425
371
|
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
|
|
@@ -489,6 +435,9 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
489
435
|
[(id<RNCNativeScrollerInsetCoordinator>)owner nativeScrollerWillReceiveScrollCommand:self];
|
|
490
436
|
}
|
|
491
437
|
}
|
|
438
|
+
#if DEBUG
|
|
439
|
+
[self offsetDiag:[NSString stringWithFormat:@"command-scrollTo-enter-x=%.4f-y=%.4f-animated=%d", x, y, animated]];
|
|
440
|
+
#endif
|
|
492
441
|
[self stopScrolling];
|
|
493
442
|
UIEdgeInsets inset = _scrollView.adjustedContentInset;
|
|
494
443
|
CGPoint target = CGPointMake(MAX(-inset.left, MIN(x,
|
|
@@ -540,7 +489,6 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
540
489
|
- (void)didMoveToWindow
|
|
541
490
|
{
|
|
542
491
|
[super didMoveToWindow];
|
|
543
|
-
[self updateTopScrollEdge];
|
|
544
492
|
if (self.window == nil) {
|
|
545
493
|
[self stopScrolling];
|
|
546
494
|
[_pagerStickyHeights removeAllObjects];
|
|
@@ -556,9 +504,6 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
556
504
|
{
|
|
557
505
|
[self stopScrolling];
|
|
558
506
|
_recycled = YES;
|
|
559
|
-
[self updateTopScrollEdge];
|
|
560
|
-
_topOverlay = nil;
|
|
561
|
-
_appliedOverlayInset = 0;
|
|
562
507
|
_scrollView.delegate = nil;
|
|
563
508
|
++_generation;
|
|
564
509
|
_state.reset();
|
|
@@ -4,15 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
NS_ASSUME_NONNULL_BEGIN
|
|
6
6
|
|
|
7
|
-
@protocol RNCPagerViewNestedOverlayObserver <NSObject>
|
|
8
|
-
- (void)outerPagerDidUpdateOverlayOwner:(BOOL)active transitioning:(BOOL)transitioning;
|
|
9
|
-
@end
|
|
10
|
-
|
|
11
7
|
@interface RNCPagerViewComponentView : RCTViewComponentView
|
|
12
8
|
|
|
13
|
-
- (void)addNestedOverlayObserver:(id<RNCPagerViewNestedOverlayObserver>)observer;
|
|
14
|
-
- (void)removeNestedOverlayObserver:(id<RNCPagerViewNestedOverlayObserver>)observer;
|
|
15
|
-
|
|
16
9
|
@end
|
|
17
10
|
|
|
18
11
|
NS_ASSUME_NONNULL_END
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
|
|
13
13
|
using namespace facebook::react;
|
|
14
14
|
|
|
15
|
-
|
|
16
15
|
@interface RNCPagerViewComponentView () <RCTRNCViewPagerViewProtocol, UIPageViewControllerDataSource, UIPageViewControllerDelegate, UIScrollViewDelegate, UIGestureRecognizerDelegate>
|
|
17
16
|
|
|
18
17
|
@property(nonatomic, strong) UIPageViewController *nativePageViewController;
|
|
@@ -40,32 +39,10 @@ using namespace facebook::react;
|
|
|
40
39
|
NSInteger _pendingGoToIndex;
|
|
41
40
|
// _nestedScrollEnabled: when YES, this inner PagerView coordinates gestures with an outer PagerView
|
|
42
41
|
BOOL _nestedScrollEnabled;
|
|
43
|
-
NSHashTable<id<RNCPagerViewNestedOverlayObserver>> *_nestedOverlayObservers;
|
|
44
42
|
// _blockerGesture: gate gesture that blocks inner scroll at page boundaries when nested
|
|
45
43
|
UIPanGestureRecognizer *_blockerGesture;
|
|
46
44
|
}
|
|
47
45
|
|
|
48
|
-
- (void)notifyNestedOverlayObservers {
|
|
49
|
-
UIView *activePage = _currentIndex >= 0 &&
|
|
50
|
-
_currentIndex < _nativeChildrenViewControllers.count
|
|
51
|
-
? _nativeChildrenViewControllers[_currentIndex].view : nil;
|
|
52
|
-
for (id<RNCPagerViewNestedOverlayObserver> observer in _nestedOverlayObservers) {
|
|
53
|
-
BOOL active = activePage != nil &&
|
|
54
|
-
[(UIView *)observer isDescendantOfView:activePage];
|
|
55
|
-
[observer outerPagerDidUpdateOverlayOwner:active transitioning:transitioning || scrollView.dragging || scrollView.decelerating];
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
- (void)addNestedOverlayObserver:(id<RNCPagerViewNestedOverlayObserver>)observer {
|
|
60
|
-
if (_nestedOverlayObservers == nil) _nestedOverlayObservers = [NSHashTable weakObjectsHashTable];
|
|
61
|
-
[_nestedOverlayObservers addObject:observer];
|
|
62
|
-
[self notifyNestedOverlayObservers];
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
- (void)removeNestedOverlayObserver:(id<RNCPagerViewNestedOverlayObserver>)observer {
|
|
66
|
-
[_nestedOverlayObservers removeObject:observer];
|
|
67
|
-
}
|
|
68
|
-
|
|
69
46
|
// Needed because of this: https://github.com/facebook/react-native/pull/37274
|
|
70
47
|
+ (void)load
|
|
71
48
|
{
|
|
@@ -181,7 +158,6 @@ using namespace facebook::react;
|
|
|
181
158
|
-(void)prepareForRecycle {
|
|
182
159
|
_isBeingRecycled = YES;
|
|
183
160
|
_generation++;
|
|
184
|
-
[_nestedOverlayObservers removeAllObjects];
|
|
185
161
|
|
|
186
162
|
// Cancel any ongoing transitions
|
|
187
163
|
if (_nativePageViewController != nil && transitioning) {
|
|
@@ -363,7 +339,6 @@ using namespace facebook::react;
|
|
|
363
339
|
_pendingGoToIndex = -1;
|
|
364
340
|
|
|
365
341
|
transitioning = YES;
|
|
366
|
-
[self notifyNestedOverlayObservers];
|
|
367
342
|
|
|
368
343
|
__weak RNCPagerViewComponentView *weakSelf = self;
|
|
369
344
|
NSUInteger capturedGeneration = _generation;
|
|
@@ -395,7 +370,6 @@ using namespace facebook::react;
|
|
|
395
370
|
|
|
396
371
|
[strongSelf enableSwipe];
|
|
397
372
|
strongSelf->_currentIndex = index;
|
|
398
|
-
[strongSelf notifyNestedOverlayObservers];
|
|
399
373
|
if (strongSelf->_eventEmitter != nullptr ) {
|
|
400
374
|
const auto eventEmitter = [strongSelf pagerEventEmitter];
|
|
401
375
|
if (eventEmitter) {
|
|
@@ -448,7 +422,6 @@ using namespace facebook::react;
|
|
|
448
422
|
if (_isBeingRecycled) {
|
|
449
423
|
return;
|
|
450
424
|
}
|
|
451
|
-
[self notifyNestedOverlayObservers];
|
|
452
425
|
const auto eventEmitter = [self pagerEventEmitter];
|
|
453
426
|
if (!eventEmitter) {
|
|
454
427
|
return;
|
|
@@ -482,12 +455,7 @@ using namespace facebook::react;
|
|
|
482
455
|
}
|
|
483
456
|
}
|
|
484
457
|
|
|
485
|
-
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
|
|
486
|
-
if (!decelerate) [self notifyNestedOverlayObservers];
|
|
487
|
-
}
|
|
488
|
-
|
|
489
458
|
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
|
|
490
|
-
[self notifyNestedOverlayObservers];
|
|
491
459
|
if (_isBeingRecycled) {
|
|
492
460
|
return;
|
|
493
461
|
}
|
|
@@ -561,14 +529,12 @@ using namespace facebook::react;
|
|
|
561
529
|
UIViewController* currentVC = [self currentlyDisplayed];
|
|
562
530
|
NSUInteger currentIndex = [_nativeChildrenViewControllers indexOfObject:currentVC];
|
|
563
531
|
_currentIndex = currentIndex;
|
|
564
|
-
[self notifyNestedOverlayObservers];
|
|
565
532
|
int position = (int) currentIndex;
|
|
566
533
|
const auto eventEmitter = [self pagerEventEmitter];
|
|
567
534
|
if (eventEmitter) {
|
|
568
535
|
eventEmitter->onPageSelected(RNCViewPagerEventEmitter::OnPageSelected{.position = static_cast<double>(position)});
|
|
569
536
|
}
|
|
570
537
|
}
|
|
571
|
-
if (!completed) [self notifyNestedOverlayObservers];
|
|
572
538
|
}
|
|
573
539
|
|
|
574
540
|
#pragma mark - UIPageViewControllerDataSource
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
#import <XCTest/XCTest.h>
|
|
2
2
|
|
|
3
3
|
#import "RNCCollapsiblePagerViewComponentView.h"
|
|
4
|
-
#import "RNCPagerViewComponentView.h"
|
|
5
4
|
#import <react/renderer/components/pagerview/Props.h>
|
|
6
5
|
|
|
7
6
|
using namespace facebook::react;
|
|
8
7
|
|
|
9
|
-
@interface RNCCollapsiblePagerViewComponentView (ScrollEdgeTesting)
|
|
10
|
-
- (void)updateTopScrollEdgeForScrollView:(UIScrollView *)scrollView;
|
|
11
|
-
@end
|
|
12
|
-
|
|
13
8
|
@interface RNCCollapsiblePagerHitTestTests : XCTestCase
|
|
14
9
|
@property (nonatomic, strong) UIView *root;
|
|
15
10
|
@property (nonatomic, strong) UIButton *toolbar;
|
|
@@ -82,48 +77,4 @@ using namespace facebook::react;
|
|
|
82
77
|
XCTAssertNil([self.pager hitTest:CGPointMake(20, 90) withEvent:nil]);
|
|
83
78
|
}
|
|
84
79
|
|
|
85
|
-
- (void)testExternalOverlayWithoutOuterPagerOwnsItsEdgeAndRegisteredOuterStillGates
|
|
86
|
-
{
|
|
87
|
-
if (@available(iOS 26.0, *)) {
|
|
88
|
-
UIWindow *window = [[UIWindow alloc] initWithFrame:self.root.bounds];
|
|
89
|
-
[window addSubview:self.root];
|
|
90
|
-
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.pager.bounds];
|
|
91
|
-
[self.pager addSubview:scrollView];
|
|
92
|
-
UIScrollEdgeEffectStyle *originalStyle = scrollView.topEdgeEffect.style;
|
|
93
|
-
[self.pager setValue:@YES forKey:@"topScrollEdgeHard"];
|
|
94
|
-
[self.pager setValue:@123 forKey:@"externalTopOverlayTag"];
|
|
95
|
-
[self.pager setValue:@64 forKey:@"externalTopOverlayHeight"];
|
|
96
|
-
[self.pager updateTopScrollEdgeForScrollView:scrollView];
|
|
97
|
-
XCTAssertEqual([self.pager valueForKey:@"topEdgeScrollView"], scrollView);
|
|
98
|
-
UIScrollEdgeElementContainerInteraction *interaction =
|
|
99
|
-
[self.pager valueForKey:@"topEdgeInteraction"];
|
|
100
|
-
XCTAssertEqual(interaction.scrollView, scrollView);
|
|
101
|
-
UIView *host = [self.pager valueForKey:@"topEdgeInteractionHost"];
|
|
102
|
-
XCTAssertTrue([host.interactions containsObject:interaction]);
|
|
103
|
-
|
|
104
|
-
RNCPagerViewComponentView *outer = [RNCPagerViewComponentView new];
|
|
105
|
-
[self.pager setValue:outer forKey:@"externalOuterPager"];
|
|
106
|
-
[self.pager setValue:@NO forKey:@"externalOuterPageActive"];
|
|
107
|
-
[self.pager updateTopScrollEdgeForScrollView:scrollView];
|
|
108
|
-
XCTAssertNil([self.pager valueForKey:@"topEdgeInteraction"]);
|
|
109
|
-
[self.pager setValue:@YES forKey:@"externalOuterPageActive"];
|
|
110
|
-
[self.pager updateTopScrollEdgeForScrollView:scrollView];
|
|
111
|
-
XCTAssertNotNil([self.pager valueForKey:@"topEdgeInteraction"]);
|
|
112
|
-
[self.pager setValue:@YES forKey:@"externalOuterTransitioning"];
|
|
113
|
-
[self.pager updateTopScrollEdgeForScrollView:scrollView];
|
|
114
|
-
XCTAssertNil([self.pager valueForKey:@"topEdgeInteraction"]);
|
|
115
|
-
|
|
116
|
-
[self.pager setValue:nil forKey:@"externalOuterPager"];
|
|
117
|
-
[self.pager updateTopScrollEdgeForScrollView:scrollView];
|
|
118
|
-
XCTAssertNotNil([self.pager valueForKey:@"topEdgeInteraction"]);
|
|
119
|
-
[self.pager removeFromSuperview];
|
|
120
|
-
XCTAssertNil([self.pager valueForKey:@"topEdgeInteraction"]);
|
|
121
|
-
XCTAssertNil([self.pager valueForKey:@"topEdgeScrollView"]);
|
|
122
|
-
XCTAssertEqual(scrollView.topEdgeEffect.style, originalStyle);
|
|
123
|
-
[self.root removeFromSuperview];
|
|
124
|
-
} else {
|
|
125
|
-
XCTSkip(@"Scroll-edge interactions require iOS 26 or newer");
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
80
|
@end
|
|
@@ -23,7 +23,6 @@ const pageKey = (child, index) => {
|
|
|
23
23
|
*/
|
|
24
24
|
export class CollapsiblePagerView extends React.PureComponent {
|
|
25
25
|
state = {
|
|
26
|
-
topOverlayHeight: 0,
|
|
27
26
|
selectedPage: clampedPageIndex(this.props.initialPage ?? 0, React.Children.toArray(this.props.children).length),
|
|
28
27
|
transientRetainedPages: []
|
|
29
28
|
};
|
|
@@ -196,10 +195,6 @@ export class CollapsiblePagerView extends React.PureComponent {
|
|
|
196
195
|
stickyHeader,
|
|
197
196
|
headerHeight,
|
|
198
197
|
stickyHeaderHeight,
|
|
199
|
-
topOverlay,
|
|
200
|
-
externalTopOverlayTag,
|
|
201
|
-
topScrollEdgeEffect,
|
|
202
|
-
topOverlayColorScheme,
|
|
203
198
|
nativeSmoothHeaderScrollEnabled,
|
|
204
199
|
pageRetentionDistance: _pageRetentionDistance,
|
|
205
200
|
onMountedPagesChanged: _onMountedPagesChanged,
|
|
@@ -216,9 +211,6 @@ export class CollapsiblePagerView extends React.PureComponent {
|
|
|
216
211
|
...nativeProps
|
|
217
212
|
} = this.props;
|
|
218
213
|
const pages = this.pages();
|
|
219
|
-
const supportsTopScrollEdge = Platform.OS === "ios" && parseInt(String(Platform.Version), 10) >= 26;
|
|
220
|
-
const hardScrollEdge = supportsTopScrollEdge && topScrollEdgeEffect === "hard";
|
|
221
|
-
const hasTopOverlay = hardScrollEdge && topOverlay != null;
|
|
222
214
|
const pageKeys = pages.map(pageKey);
|
|
223
215
|
const retained = new Set(this.mountedPages(pages.length));
|
|
224
216
|
const deducedLayoutDirection = !layoutDirection || layoutDirection === "locale" ? I18nManager.isRTL ? "rtl" : "ltr" : layoutDirection;
|
|
@@ -243,10 +235,6 @@ export class CollapsiblePagerView extends React.PureComponent {
|
|
|
243
235
|
offscreenPageLimit: Math.max(1, offscreenPageLimit ?? 1),
|
|
244
236
|
headerHeight: Math.max(0, Math.round(headerHeight)),
|
|
245
237
|
stickyHeaderHeight: Math.max(0, Math.round(stickyHeaderHeight)),
|
|
246
|
-
topScrollEdgeHard: hardScrollEdge,
|
|
247
|
-
topOverlayColorScheme: hardScrollEdge ? topOverlayColorScheme : undefined,
|
|
248
|
-
topOverlayHeight: hasTopOverlay ? this.state.topOverlayHeight : 0,
|
|
249
|
-
externalTopOverlayTag: supportsTopScrollEdge && !hasTopOverlay ? externalTopOverlayTag ?? 0 : 0,
|
|
250
238
|
nativeSmoothHeaderScrollEnabled: nativeSmoothHeaderScrollEnabled,
|
|
251
239
|
pageKeys: JSON.stringify(pageKeys),
|
|
252
240
|
retainedPages: JSON.stringify([...retained]),
|
|
@@ -285,21 +273,7 @@ export class CollapsiblePagerView extends React.PureComponent {
|
|
|
285
273
|
height: Math.max(0, Math.round(stickyHeaderHeight))
|
|
286
274
|
}],
|
|
287
275
|
children: stickyHeader
|
|
288
|
-
}, "sticky-header"),
|
|
289
|
-
collapsable: false,
|
|
290
|
-
style: [styles.slot, {
|
|
291
|
-
bottom: undefined
|
|
292
|
-
}],
|
|
293
|
-
onLayout: event => {
|
|
294
|
-
const height = Math.ceil(event.nativeEvent.layout.height);
|
|
295
|
-
if (this.state.topOverlayHeight !== height) {
|
|
296
|
-
this.setState({
|
|
297
|
-
topOverlayHeight: height
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
},
|
|
301
|
-
children: topOverlay
|
|
302
|
-
}, "top-overlay") : null, pages.map((page, index) => /*#__PURE__*/_jsx(View, {
|
|
276
|
+
}, "sticky-header"), pages.map((page, index) => /*#__PURE__*/_jsx(View, {
|
|
303
277
|
collapsable: false,
|
|
304
278
|
style: [styles.slot,
|
|
305
279
|
// Android translates the expanded pager while consuming header scroll.
|
|
@@ -483,10 +483,6 @@ export class CollapsiblePagerView extends React.PureComponent {
|
|
|
483
483
|
children: _children,
|
|
484
484
|
header,
|
|
485
485
|
stickyHeader,
|
|
486
|
-
topOverlay: _topOverlay,
|
|
487
|
-
topScrollEdgeEffect: _topScrollEdgeEffect,
|
|
488
|
-
topOverlayColorScheme: _topOverlayColorScheme,
|
|
489
|
-
externalTopOverlayTag: _externalTopOverlayTag,
|
|
490
486
|
headerHeight: _headerHeight,
|
|
491
487
|
stickyHeaderHeight: _stickyHeaderHeight,
|
|
492
488
|
pageRetentionDistance: _pageRetentionDistance,
|
|
@@ -43,7 +43,6 @@ export type OnNativeTabPressEventData = Readonly<{
|
|
|
43
43
|
position: Int32;
|
|
44
44
|
key: string;
|
|
45
45
|
}>;
|
|
46
|
-
export type OnScrollEdgeCapabilityEventData = Readonly<{ available: boolean }>;
|
|
47
46
|
|
|
48
47
|
/**
|
|
49
48
|
* Internal native props. Consumers should use CollapsiblePagerViewProps from
|
|
@@ -59,10 +58,6 @@ export interface NativeProps extends ViewProps {
|
|
|
59
58
|
nativeSmoothHeaderScrollEnabled?: WithDefault<boolean, false>;
|
|
60
59
|
headerHeight?: Int32;
|
|
61
60
|
stickyHeaderHeight?: Int32;
|
|
62
|
-
topScrollEdgeHard?: WithDefault<boolean, false>;
|
|
63
|
-
topOverlayColorScheme?: string;
|
|
64
|
-
topOverlayHeight?: WithDefault<Double, 0>;
|
|
65
|
-
externalTopOverlayTag?: WithDefault<Int32, 0>;
|
|
66
61
|
pageKeys?: string;
|
|
67
62
|
retainedPages?: string;
|
|
68
63
|
nativeTabBarItems?: string;
|
|
@@ -85,7 +80,6 @@ export interface NativeProps extends ViewProps {
|
|
|
85
80
|
onCollapsibleStateChanged?: DirectEventHandler<OnCollapsibleStateChangedEventData>;
|
|
86
81
|
onNativeTabPress?: DirectEventHandler<OnNativeTabPressEventData>;
|
|
87
82
|
onNativeSubHeaderPress?: DirectEventHandler<OnNativeTabPressEventData>;
|
|
88
|
-
onScrollEdgeCapability?: DirectEventHandler<OnScrollEdgeCapabilityEventData>;
|
|
89
83
|
}
|
|
90
84
|
|
|
91
85
|
type CollapsiblePagerViewNativeType = HostComponent<NativeProps>;
|