@onekeyfe/react-native-pager-view 3.0.168 → 3.0.170

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/docs/SPEC.md CHANGED
@@ -254,12 +254,26 @@ start a refresh gesture. Non-smooth refresh direction behavior remains unchanged
254
254
  iOS nested horizontal ownership MUST include enabled nonpaging horizontal-only
255
255
  ScrollViews, with content wider than bounds and no vertical content overflow.
256
256
  Both pan holding and failure dependencies MUST use the same eligibility. A
257
- failure dependency on the Pager guard MUST apply only to the horizontal pan;
257
+ failure dependency on the Pager guard MUST apply only to the horizontal pan,
258
+ both on the root Pager ScrollView and on eligible nested horizontal ScrollViews;
258
259
  independent non-pan recognizers, including stationary native long presses, MUST
259
- remain able to recognize before finger-up while Pager intent is pending. A
260
+ remain able to deliver their target actions before finger-up while Pager intent
261
+ is pending. A
260
262
  second touch in the pending Pager guard fails recognition; an already active
261
263
  Pager guard cancels and restores each held pan's original minimum touch count.
262
264
 
265
+ In iOS smooth-header mode, a confirmed horizontal guard displacement of at
266
+ least 32 points, exceeding vertical displacement, MUST cancel the active React
267
+ press on its one eligible content Surface before finger-up even when the pager
268
+ pan has not begun. The content observer stays non-recognizing and resets only
269
+ that Surface's already Began/Changed touch handler. Shared-header native controls,
270
+ stationary taps, pending small-horizontal-to-vertical gestures, and multi-touch
271
+ sequences retain their existing ownership. Cancellation is emitted at most once
272
+ for the tracked touch; end, cancellation, and reuse clear that touch's state.
273
+ The attached UIKit regression intentionally constrains the real pager pan to two
274
+ required touches to prove the no-pan path; it is distinct from natural Home OS
275
+ input, coalesced-event delivery, and physical-device acceptance.
276
+
263
277
  ## Native tab-press animation default
264
278
 
265
279
  Behavior change: previously an omitted `nativeTabPressAnimationEnabled` animated
@@ -1070,11 +1070,12 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
1070
1070
 
1071
1071
  @end
1072
1072
 
1073
- // This observer never competes for scrolling. The owning pager explicitly
1074
- // recognizes it only after its real pan begins, so UIKit terminates RN presses.
1073
+ // This observer never competes for scrolling. RN presses are terminated when
1074
+ // the pager pan begins or the existing guard resolves horizontal intent.
1075
1075
  @interface RNCCollapsiblePagerContentPressCancellationGestureRecognizer : UIGestureRecognizer
1076
1076
  @property (nonatomic, weak) UIGestureRecognizer *reactTouchHandler;
1077
1077
  - (void)cancelPressForPan:(UIPanGestureRecognizer *)pan;
1078
+ - (void)cancelPressForHorizontalTouch:(UITouch *)touch event:(UIEvent *)event;
1078
1079
  - (void)observeIncomingTouch:(UITouch *)touch;
1079
1080
  @end
1080
1081
 
@@ -1084,6 +1085,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
1084
1085
  BOOL _multipleSurfaceTouchesObserved;
1085
1086
  CGPoint _trackedTouchStartPoint;
1086
1087
  BOOL _didCancelReactPressForVerticalMovement;
1088
+ BOOL _didCancelReactPressForHorizontalMovement;
1087
1089
  }
1088
1090
 
1089
1091
  - (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer *)other
@@ -1122,6 +1124,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
1122
1124
  _trackedTouch = touches.anyObject;
1123
1125
  _trackedTouchStartPoint = [_trackedTouch locationInView:self.view];
1124
1126
  _didCancelReactPressForVerticalMovement = NO;
1127
+ _didCancelReactPressForHorizontalMovement = NO;
1125
1128
  [self recordEvent:event];
1126
1129
  }
1127
1130
 
@@ -1153,6 +1156,31 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
1153
1156
  handler.enabled = YES;
1154
1157
  }
1155
1158
 
1159
+ - (void)cancelPressForHorizontalTouch:(UITouch *)touch event:(UIEvent *)event
1160
+ {
1161
+ [self recordEvent:event];
1162
+ UIGestureRecognizer *handler = self.reactTouchHandler;
1163
+ BOOL eligible = touch != nil && touch == _trackedTouch &&
1164
+ touch.phase != UITouchPhaseEnded && touch.phase != UITouchPhaseCancelled &&
1165
+ self.state == UIGestureRecognizerStatePossible &&
1166
+ !_multipleSurfaceTouchesObserved && !_didCancelReactPressForHorizontalMovement &&
1167
+ handler.view != nil && self.view == handler.view.superview &&
1168
+ (handler.state == UIGestureRecognizerStateBegan || handler.state == UIGestureRecognizerStateChanged);
1169
+ NSUInteger activeTouches = 0;
1170
+ for (UITouch *activeTouch in event.allTouches) {
1171
+ if (activeTouch.phase == UITouchPhaseEnded || activeTouch.phase == UITouchPhaseCancelled) continue;
1172
+ if ([activeTouch.view isDescendantOfView:handler.view]) ++activeTouches;
1173
+ }
1174
+ if (!eligible || activeTouches != 1) return;
1175
+ // A coalesced move can resolve horizontal intent before the pager pan begins.
1176
+ _didCancelReactPressForHorizontalMovement = YES;
1177
+ #if DEBUG
1178
+ RNCCollapsiblePagerLog(@"content-press-cancel axis=horizontal");
1179
+ #endif
1180
+ handler.enabled = NO;
1181
+ handler.enabled = YES;
1182
+ }
1183
+
1156
1184
  - (void)cancelPressForPan:(UIPanGestureRecognizer *)pan
1157
1185
  {
1158
1186
  UIGestureRecognizer *handler = self.reactTouchHandler;
@@ -1185,6 +1213,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
1185
1213
  _trackedTouch = nil;
1186
1214
  _touchEvent = nil;
1187
1215
  _didCancelReactPressForVerticalMovement = NO;
1216
+ _didCancelReactPressForHorizontalMovement = NO;
1188
1217
  }
1189
1218
 
1190
1219
  - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
@@ -1195,6 +1224,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
1195
1224
  _trackedTouch = nil;
1196
1225
  _touchEvent = nil;
1197
1226
  _didCancelReactPressForVerticalMovement = NO;
1227
+ _didCancelReactPressForHorizontalMovement = NO;
1198
1228
  }
1199
1229
 
1200
1230
  - (void)reset
@@ -1204,6 +1234,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
1204
1234
  _touchEvent = nil;
1205
1235
  _multipleSurfaceTouchesObserved = NO;
1206
1236
  _didCancelReactPressForVerticalMovement = NO;
1237
+ _didCancelReactPressForHorizontalMovement = NO;
1207
1238
  }
1208
1239
 
1209
1240
  @end
@@ -1238,6 +1269,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
1238
1269
 
1239
1270
  @interface RNCCollapsiblePagerVerticalPagerGuardGestureRecognizer : UIGestureRecognizer
1240
1271
  @property (nonatomic, weak) UIPanGestureRecognizer *pagerPan;
1272
+ @property (nonatomic, weak) RNCCollapsiblePagerContentPressCancellationGestureRecognizer *contentPressCancellation;
1241
1273
  - (CGPoint)translationInView:(UIView *)view;
1242
1274
  - (CGPoint)velocityInView:(UIView *)view;
1243
1275
  @end
@@ -1333,6 +1365,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
1333
1365
  RNCCollapsiblePagerLog([NSString stringWithFormat:
1334
1366
  @"gesture-intent axis=%@ dx=%.2f dy=%.2f held-pans=%lu",
1335
1367
  dy > dx ? @"vertical" : @"horizontal", dx, dy, (unsigned long)_heldPans.count]);
1368
+ if (dx >= 32 && dx > dy) {
1369
+ [self.contentPressCancellation cancelPressForHorizontalTouch:_trackedTouch event:event];
1370
+ }
1336
1371
  self.state = UIGestureRecognizerStateBegan;
1337
1372
  if (self.state == UIGestureRecognizerStateFailed) [self restoreHeldPans];
1338
1373
  }
@@ -2716,6 +2751,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
2716
2751
  [_contentPressCancellationGesture.view removeGestureRecognizer:_contentPressCancellationGesture];
2717
2752
  }
2718
2753
  _contentPressCancellationGesture.reactTouchHandler = nil;
2754
+ _verticalPagerGesture.contentPressCancellation = nil;
2719
2755
  _contentPressCancellationGesture = nil;
2720
2756
  if (_sharedHeaderPressCancellationGesture.view != nil) {
2721
2757
  [_sharedHeaderPressCancellationGesture.view
@@ -2734,9 +2770,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
2734
2770
  {
2735
2771
  if (_pagerScrollView != nil && _verticalPagerGesture != nil) {
2736
2772
  _verticalPagerGesture.pagerPan = _pagerScrollView.panGestureRecognizer;
2737
- for (UIGestureRecognizer *gesture in _pagerScrollView.gestureRecognizers) {
2738
- [gesture requireGestureRecognizerToFail:_verticalPagerGesture];
2739
- }
2773
+ [_pagerScrollView.panGestureRecognizer requireGestureRecognizerToFail:_verticalPagerGesture];
2740
2774
  }
2741
2775
  }
2742
2776
 
@@ -2814,6 +2848,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
2814
2848
  _contentPressCancellationGesture.delaysTouchesEnded = NO;
2815
2849
  }
2816
2850
  _contentPressCancellationGesture.reactTouchHandler = reactTouchHandler;
2851
+ _verticalPagerGesture.contentPressCancellation = _contentPressCancellationGesture;
2817
2852
  [gestureHost addGestureRecognizer:_contentPressCancellationGesture];
2818
2853
  _reactTouchHandler = reactTouchHandler;
2819
2854
  _sharedHeaderPressCancellationGestureHost = gestureHost;
@@ -70,6 +70,9 @@ typedef struct __IOHIDEvent *RNCTestHIDEventRef;
70
70
  - (UITouch *)beginAt:(CGPoint)windowPoint;
71
71
  - (void)move:(UITouch *)touch to:(CGPoint)windowPoint;
72
72
  - (void)end:(UITouch *)touch;
73
+ - (void)cancel:(UITouch *)touch;
74
+ - (void)singleMoveKeepingDownTimestamp:(UITouch *)touch to:(CGPoint)point;
75
+ - (void)endAfterTwoFrames:(UITouch *)touch;
73
76
  @end
74
77
 
75
78
  @implementation RNCSynthesizedTouchDriver {
@@ -198,6 +201,25 @@ typedef struct __IOHIDEvent *RNCTestHIDEventRef;
198
201
  [self sendChangingTouch:touch phase:UITouchPhaseEnded];
199
202
  }
200
203
 
204
+ - (void)cancel:(UITouch *)touch
205
+ {
206
+ [self sendChangingTouch:touch phase:UITouchPhaseCancelled];
207
+ }
208
+
209
+ - (void)singleMoveKeepingDownTimestamp:(UITouch *)touch to:(CGPoint)point
210
+ {
211
+ // One controlled delivered MOVE shares DOWN's touch timestamp.
212
+ // This is controlled UIWindow delivery, not OS input or coalescing coverage.
213
+ _timestamp -= 1.0 / 60.0;
214
+ [self move:touch to:point];
215
+ }
216
+
217
+ - (void)endAfterTwoFrames:(UITouch *)touch
218
+ {
219
+ _timestamp += 1.0 / 60.0;
220
+ [self end:touch];
221
+ }
222
+
201
223
  @end
202
224
 
203
225
  // Private declarations access the production class compiled into the pod.
@@ -581,14 +603,32 @@ typedef struct __IOHIDEvent *RNCTestHIDEventRef;
581
603
  @interface RNCRecordingSurfaceTouchHandler : RCTSurfaceTouchHandler
582
604
  @property (nonatomic, strong) NSMutableArray<NSString *> *events;
583
605
  @property (nonatomic, assign) NSInteger registeredTouches;
606
+ @property (nonatomic) NSUInteger enabledNoWithRegisteredTouch;
607
+ @property (nonatomic, strong) NSMutableArray<NSDictionary *> *timeline;
584
608
  @end
585
609
 
586
610
  @implementation RNCRecordingSurfaceTouchHandler
611
+ - (void)recordTimeline:(NSString *)event
612
+ {
613
+ if (self.timeline == nil) self.timeline = [NSMutableArray new];
614
+ [self.timeline addObject:@{@"event": event, @"state": @(self.state),
615
+ @"registered": @(self.registeredTouches),
616
+ @"enabled": @(self.enabled)}];
617
+ }
618
+ - (void)setEnabled:(BOOL)enabled
619
+ {
620
+ if (!enabled && self.registeredTouches > 0) {
621
+ self.enabledNoWithRegisteredTouch += 1;
622
+ [self recordTimeline:@"enabled-NO-before-super"];
623
+ }
624
+ [super setEnabled:enabled];
625
+ }
587
626
  - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
588
627
  {
589
628
  [super touchesBegan:touches withEvent:event];
590
629
  self.registeredTouches += touches.count;
591
630
  [self.events addObject:@"began"];
631
+ [self recordTimeline:@"began"];
592
632
  }
593
633
  - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
594
634
  {
@@ -606,13 +646,17 @@ typedef struct __IOHIDEvent *RNCTestHIDEventRef;
606
646
  [super touchesCancelled:touches withEvent:event];
607
647
  self.registeredTouches -= touches.count;
608
648
  [self.events addObject:@"cancelled"];
649
+ [self recordTimeline:@"cancelled"];
609
650
  }
610
651
  - (void)reset
611
652
  {
612
653
  NSInteger pending = self.registeredTouches;
613
654
  [super reset];
614
655
  self.registeredTouches = 0;
615
- if (pending > 0) [self.events addObject:@"reset-cancelled"];
656
+ if (pending > 0) {
657
+ [self.events addObject:@"reset-cancelled"];
658
+ [self recordTimeline:@"reset-cancelled"];
659
+ }
616
660
  }
617
661
  @end
618
662
 
@@ -624,6 +668,7 @@ typedef struct __IOHIDEvent *RNCTestHIDEventRef;
624
668
  @property (nonatomic, strong) RNCSynthesizedTouchDriver *driver;
625
669
  @property (nonatomic, strong) UIView *firstPage;
626
670
  @property (nonatomic) NSUInteger longPressBegins;
671
+ @property (nonatomic) NSUInteger pagerPanBegins;
627
672
  @end
628
673
 
629
674
  @implementation RNCCollapsiblePagerPressCancellationDeliveryTests
@@ -711,6 +756,127 @@ typedef struct __IOHIDEvent *RNCTestHIDEventRef;
711
756
  return CGPointMake(200, 600);
712
757
  }
713
758
 
759
+ - (void)assertDirectPagerForRegression
760
+ {
761
+ XCTAssertTrue([[self.pager valueForKey:@"directNativePagerEnabled"] boolValue]);
762
+ UIScrollView *pager = [self.pager valueForKey:@"pagerScrollView"];
763
+ XCTAssertTrue([pager isKindOfClass:NSClassFromString(@"RNCCollapsiblePagerDirectScrollView")]);
764
+ }
765
+
766
+ - (void)recordPagerPan:(UIPanGestureRecognizer *)pan
767
+ {
768
+ if (pan.state == UIGestureRecognizerStateBegan) self.pagerPanBegins += 1;
769
+ }
770
+
771
+ - (UITouch *)beginSingleMoveHorizontalSequence
772
+ {
773
+ [self assertFixtureIsReadyForDelivery];
774
+ [self assertDirectPagerForRegression];
775
+ UIScrollView *pager = [self.pager valueForKey:@"pagerScrollView"];
776
+ // A controlled absence of pager-pan recognition, independent of UIKit event timing.
777
+ // This does not model the natural Home OS recognizer configuration.
778
+ pager.panGestureRecognizer.minimumNumberOfTouches = 2;
779
+ XCTAssertTrue(pager.panGestureRecognizer.enabled);
780
+ XCTAssertEqual(pager.panGestureRecognizer.minimumNumberOfTouches, 2);
781
+ [pager.panGestureRecognizer addTarget:self action:@selector(recordPagerPan:)];
782
+ CGPoint point = CGPointMake(350, 600);
783
+ UIView *hit = [self.window hitTest:point withEvent:nil];
784
+ XCTAssertTrue(hit == self.firstPage || [hit isDescendantOfView:self.firstPage]);
785
+ UITouch *touch = [self.driver beginAt:point];
786
+ XCTAssertEqual(self.handler.registeredTouches, 1);
787
+ XCTAssertTrue(self.handler.state == UIGestureRecognizerStateBegan ||
788
+ self.handler.state == UIGestureRecognizerStateChanged);
789
+ UIGestureRecognizer *observer = [self.pager valueForKey:@"contentPressCancellationGesture"];
790
+ XCTAssertEqual(observer.state, UIGestureRecognizerStatePossible);
791
+ XCTAssertEqual([observer valueForKey:@"reactTouchHandler"], self.handler);
792
+ XCTAssertEqual(observer.view, self.handler.view.superview);
793
+ XCTAssertTrue([touch.view isDescendantOfView:self.handler.view]);
794
+ NSTimeInterval down = touch.timestamp;
795
+ [self.driver singleMoveKeepingDownTimestamp:touch to:CGPointMake(25, 600)];
796
+ XCTAssertEqualWithAccuracy(touch.timestamp, down, 0.000001);
797
+ XCTAssertEqual(self.pagerPanBegins, 0, @"A pager-pan callback cannot explain this cancellation");
798
+ XCTAssertFalse(pager.isDragging);
799
+ XCTAssertTrue([[observer valueForKey:@"didCancelReactPressForHorizontalMovement"] boolValue]);
800
+ XCTAssertEqual(self.handler.registeredTouches, 0);
801
+ NSUInteger cancellations = 0;
802
+ for (NSString *event in self.handler.events) {
803
+ if ([event isEqualToString:@"reset-cancelled"] || [event isEqualToString:@"cancelled"]) ++cancellations;
804
+ }
805
+ XCTAssertEqual(cancellations, 1, @"Exactly one real RN cancellation must occur before UP: %@", self.handler.events);
806
+ XCTAssertEqual(self.handler.enabledNoWithRegisteredTouch, 1);
807
+ NSPredicate *disabled = [NSPredicate predicateWithFormat:@"event == %@", @"enabled-NO-before-super"];
808
+ NSArray *disableCalls = [self.handler.timeline filteredArrayUsingPredicate:disabled];
809
+ XCTAssertEqual(disableCalls.count, 1);
810
+ XCTAssertEqualObjects(disableCalls.firstObject[@"registered"], @1);
811
+ XCTAssertFalse([self.handler.events containsObject:@"ended"]);
812
+ NSLog(@"[PagerPressDelivery] case=%@ oneMoveH=325 constrainedMinTouches=2 panBegins=%lu registered=%ld cancellationCount=%lu enabledNO=%lu events=%@ timeline=%@",
813
+ self.name, (unsigned long)self.pagerPanBegins, (long)self.handler.registeredTouches,
814
+ (unsigned long)cancellations, (unsigned long)self.handler.enabledNoWithRegisteredTouch,
815
+ self.handler.events, self.handler.timeline);
816
+ return touch;
817
+ }
818
+
819
+ - (void)testSingleDeliveredHorizontalMoveCancelsBeforeUpWithoutPagerPanBegan
820
+ {
821
+ UITouch *touch = [self beginSingleMoveHorizontalSequence];
822
+ [self.driver endAfterTwoFrames:touch];
823
+ XCTAssertEqual(((UIScrollView *)[self.pager valueForKey:@"pagerScrollView"]).panGestureRecognizer.minimumNumberOfTouches, 2);
824
+ XCTAssertEqual(self.pagerPanBegins, 0);
825
+ XCTAssertFalse([self.handler.events containsObject:@"ended"]);
826
+ }
827
+
828
+ - (void)testSingleMoveHorizontalUpAllowsNextStationaryTap
829
+ {
830
+ UITouch *touch = [self beginSingleMoveHorizontalSequence];
831
+ [self.driver endAfterTwoFrames:touch];
832
+ XCTAssertEqual(self.pagerPanBegins, 0);
833
+ XCTAssertEqual(((UIScrollView *)[self.pager valueForKey:@"pagerScrollView"]).panGestureRecognizer.minimumNumberOfTouches, 2);
834
+ [self drainMainQueue];
835
+ [self.handler.events removeAllObjects];
836
+ UITouch *next = [self.driver beginAt:[self pressPoint]];
837
+ [self.driver end:next];
838
+ XCTAssertEqualObjects(self.handler.events, (@[@"began", @"ended"]));
839
+ XCTAssertEqual(self.handler.registeredTouches, 0);
840
+ }
841
+
842
+ - (void)testSingleMoveHorizontalCancelAllowsNextStationaryTap
843
+ {
844
+ UITouch *touch = [self beginSingleMoveHorizontalSequence];
845
+ [self.driver cancel:touch];
846
+ XCTAssertEqual(self.pagerPanBegins, 0);
847
+ XCTAssertEqual(((UIScrollView *)[self.pager valueForKey:@"pagerScrollView"]).panGestureRecognizer.minimumNumberOfTouches, 2);
848
+ [self drainMainQueue];
849
+ [self.handler.events removeAllObjects];
850
+ UITouch *next = [self.driver beginAt:[self pressPoint]];
851
+ [self.driver end:next];
852
+ XCTAssertEqualObjects(self.handler.events, (@[@"began", @"ended"]));
853
+ XCTAssertEqual(self.handler.registeredTouches, 0);
854
+ }
855
+
856
+ - (void)testSecondActiveTouchOnSameSurfaceBlocksSingleMoveHorizontalReset
857
+ {
858
+ [self assertFixtureIsReadyForDelivery];
859
+ [self assertDirectPagerForRegression];
860
+ UIScrollView *pager = [self.pager valueForKey:@"pagerScrollView"];
861
+ pager.panGestureRecognizer.minimumNumberOfTouches = 2;
862
+ XCTAssertTrue(pager.panGestureRecognizer.enabled);
863
+ UIView *sibling = [RCTViewComponentView new];
864
+ sibling.frame = CGRectMake(0, 40, 100, 80);
865
+ [self.surface addSubview:sibling];
866
+ UITouch *first = [self.driver beginAt:CGPointMake(350, 600)];
867
+ UITouch *second = [self.driver beginAt:CGPointMake(50, 80)];
868
+ XCTAssertEqual(self.handler.registeredTouches, 2);
869
+ [self.driver singleMoveKeepingDownTimestamp:first to:CGPointMake(25, 600)];
870
+ XCTAssertEqual(self.handler.registeredTouches, 2);
871
+ XCTAssertFalse([self.handler.events containsObject:@"reset-cancelled"]);
872
+ XCTAssertFalse([self.handler.events containsObject:@"cancelled"]);
873
+ XCTAssertEqual(self.handler.enabledNoWithRegisteredTouch, 0);
874
+ [self.driver end:second];
875
+ [self.driver end:first];
876
+ XCTAssertEqual(pager.panGestureRecognizer.minimumNumberOfTouches, 2);
877
+ XCTAssertEqual(self.handler.registeredTouches, 0);
878
+ }
879
+
714
880
  - (void)testHorizontalPagerDragCancelsReactPressBeforeFingerLifts
715
881
  {
716
882
  [self assertFixtureIsReadyForDelivery];
@@ -845,6 +1011,37 @@ typedef struct __IOHIDEvent *RNCTestHIDEventRef;
845
1011
  return point;
846
1012
  }
847
1013
 
1014
+ - (void)testRootPagerStationaryNativeLongPressRecognizesBeforeFingerUp
1015
+ {
1016
+ [self assertFixtureIsReadyForDelivery];
1017
+ [self assertDirectPagerForRegression];
1018
+ UIScrollView *pager = [self.pager valueForKey:@"pagerScrollView"];
1019
+ UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]
1020
+ initWithTarget:self action:@selector(nestedLongPress:)];
1021
+ longPress.minimumPressDuration = 0.1;
1022
+ [pager addGestureRecognizer:longPress];
1023
+ // Exercise production wiring after a root recognizer is attached.
1024
+ ((void (*)(id, SEL))objc_msgSend)(self.pager, NSSelectorFromString(@"connectVerticalPagerGuard"));
1025
+ UITouch *touch = [self.driver beginAt:[self pressPoint]];
1026
+ UIGestureRecognizer *guard = [self.pager valueForKey:@"verticalPagerGesture"];
1027
+ XCTAssertEqual(guard.state, UIGestureRecognizerStatePossible);
1028
+ XCTAssertEqual(pager.panGestureRecognizer.minimumNumberOfTouches, 2);
1029
+ XCTestExpectation *hold = [self expectationWithDescription:@"root stationary finger held"];
1030
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)),
1031
+ dispatch_get_main_queue(), ^{ [hold fulfill]; });
1032
+ [self waitForExpectations:@[hold] timeout:2];
1033
+ NSLog(@"[PagerPressDelivery] rootLongPress begins=%lu state=%ld touchPhase=%ld guardState=%ld pagerPanMinTouches=%lu",
1034
+ (unsigned long)self.longPressBegins, (long)longPress.state, (long)touch.phase,
1035
+ (long)guard.state, (unsigned long)pager.panGestureRecognizer.minimumNumberOfTouches);
1036
+ // The UIKit target action must arrive before the real window receives UP.
1037
+ XCTAssertEqual(self.longPressBegins, 1);
1038
+ XCTAssertEqual(touch.phase, UITouchPhaseBegan);
1039
+ XCTAssertEqual(guard.state, UIGestureRecognizerStatePossible);
1040
+ XCTAssertEqual(pager.panGestureRecognizer.minimumNumberOfTouches, 2);
1041
+ [self.driver end:touch];
1042
+ XCTAssertEqual(pager.panGestureRecognizer.minimumNumberOfTouches, 1);
1043
+ }
1044
+
848
1045
  - (void)testNestedStationaryNativeLongPressRecognizesBeforeFingerUp
849
1046
  {
850
1047
  UIScrollView *scroll = [self mountNestedHorizontalScrollView];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/react-native-pager-view",
3
- "version": "3.0.168",
3
+ "version": "3.0.170",
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.168",
75
+ "@onekeyfe/react-native-native-logger": "3.0.170",
76
76
  "react": "*",
77
77
  "react-native": "*"
78
78
  },