@onekeyfe/react-native-pager-view 3.0.157-alpha.255 → 3.0.157-alpha.256

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
@@ -3,6 +3,31 @@
3
3
  Existing PagerView and CollapsiblePagerView APIs are documented in README.md.
4
4
  The sections below define native coordination boundaries without new React props.
5
5
 
6
+ ## iOS refresh indicator placement
7
+
8
+ Status: source implementation; real refresh-cycle acceptance pending.
9
+ In smooth-header mode, the pager shifts the refresh control's sublayers upward
10
+ by its header-plus-sticky height. It does not change the refresh control frame,
11
+ bounds, scroll offset, refresh state or JavaScript callback. RN's caller-owned
12
+ `progressViewOffset` remains untouched, including dynamic updates whose bounds
13
+ origin could otherwise collide with a coordinator-applied value.
14
+
15
+ One internal state per UIRefreshControl combines weak pager-owner contributions.
16
+ It preserves the original sublayer transform and clipping setting. Repeated
17
+ attachment and header resizing replace an owner's contribution. Layer clipping
18
+ is disabled while coordinating so it cannot hide the translated indicator.
19
+ A control replacement, scroll-view release, pager recycling or destruction
20
+ removes only that owner's contribution and restores the baseline when none remain.
21
+ Controls installed after the scroller is attached are also coordinated. Native
22
+ UICollectionView controls and RN ScrollView controls use this same UIKit boundary.
23
+
24
+ Native layer observations reapply the contribution after an external sublayer
25
+ transform change, without observing or interpreting UIKit/RN bounds writes.
26
+ There is no per-frame JavaScript work. Acceptance must cover real UIKit pulling
27
+ and refresh completion, caller offset changes, late installation/replacement,
28
+ retained-page switching, clipping and nested-owner cleanup. Programmatic UIKit
29
+ unit tests do not substitute for real pull-gesture visibility acceptance.
30
+
6
31
  ## iOS shared-header hit testing
7
32
 
8
33
  Smooth-header hit forwarding is restricted to the pager's own interactive
@@ -11,6 +11,7 @@
11
11
  #import "React/RCTScrollViewComponentView.h"
12
12
  #import "React/RCTSurfaceTouchHandler.h"
13
13
  #import "React/RCTTouchHandler.h"
14
+ #import <objc/runtime.h>
14
15
 
15
16
  // OneKey patch: deduplicate structural mutation parents before scanning the page.
16
17
  #include <unordered_set>
@@ -18,6 +19,116 @@
18
19
  using namespace facebook::react;
19
20
 
20
21
  static void *RNCCollapsiblePagerContentOffsetContext = &RNCCollapsiblePagerContentOffsetContext;
22
+ static void *RNCRefreshControlLayerContext = &RNCRefreshControlLayerContext;
23
+ static void *RNCScrollRefreshControlContext = &RNCScrollRefreshControlContext;
24
+ static char RNCRefreshControlOffsetKey;
25
+
26
+ @interface RNCRefreshControlOffsetState : NSObject
27
+ + (void)setOffset:(CGFloat)offset forControl:(UIRefreshControl *)control owner:(id)owner;
28
+ + (void)removeControl:(UIRefreshControl *)control owner:(id)owner;
29
+ @end
30
+
31
+ @implementation RNCRefreshControlOffsetState {
32
+ __weak UIRefreshControl *_control;
33
+ NSMapTable<id, NSNumber *> *_offsets;
34
+ CATransform3D _baselineTransform;
35
+ CATransform3D _appliedTransform;
36
+ BOOL _originalMasksToBounds;
37
+ BOOL _applying;
38
+ BOOL _observing;
39
+ }
40
+
41
+ - (instancetype)initWithControl:(UIRefreshControl *)control
42
+ {
43
+ if ((self = [super init])) {
44
+ _control = control;
45
+ _baselineTransform = control.layer.sublayerTransform;
46
+ _originalMasksToBounds = control.layer.masksToBounds;
47
+ _offsets = [NSMapTable weakToStrongObjectsMapTable];
48
+ [control.layer addObserver:self forKeyPath:@"sublayerTransform"
49
+ options:NSKeyValueObservingOptionNew
50
+ context:RNCRefreshControlLayerContext];
51
+ [control.layer addObserver:self forKeyPath:@"masksToBounds"
52
+ options:NSKeyValueObservingOptionNew
53
+ context:RNCRefreshControlLayerContext];
54
+ _observing = YES;
55
+ }
56
+ return self;
57
+ }
58
+
59
+ - (void)apply
60
+ {
61
+ UIRefreshControl *control = _control;
62
+ if (control == nil) return;
63
+ CGFloat offset = 0;
64
+ for (NSNumber *value in _offsets.objectEnumerator) offset += value.doubleValue;
65
+ CATransform3D transform = CATransform3DConcat(
66
+ _baselineTransform, CATransform3DMakeTranslation(0, -offset, 0));
67
+ _appliedTransform = transform;
68
+ _applying = YES;
69
+ // Visual placement must not change UIKit's refresh geometry or RN's bounds
70
+ // origin (progressViewOffset). Allow the shifted indicator outside its host.
71
+ [CATransaction begin];
72
+ [CATransaction setDisableActions:YES];
73
+ control.layer.masksToBounds = _offsets.count == 0 ? _originalMasksToBounds : NO;
74
+ if (!CATransform3DEqualToTransform(control.layer.sublayerTransform, transform)) {
75
+ control.layer.sublayerTransform = transform;
76
+ }
77
+ [CATransaction commit];
78
+ _applying = NO;
79
+ }
80
+
81
+ + (void)setOffset:(CGFloat)offset forControl:(UIRefreshControl *)control owner:(id)owner
82
+ {
83
+ if (control == nil) return;
84
+ RNCRefreshControlOffsetState *state = objc_getAssociatedObject(control, &RNCRefreshControlOffsetKey);
85
+ if (state == nil) {
86
+ state = [[self alloc] initWithControl:control];
87
+ objc_setAssociatedObject(control, &RNCRefreshControlOffsetKey, state, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
88
+ }
89
+ [state->_offsets setObject:@(offset) forKey:owner];
90
+ [state apply];
91
+ }
92
+
93
+ + (void)removeControl:(UIRefreshControl *)control owner:(id)owner
94
+ {
95
+ if (control == nil) return;
96
+ RNCRefreshControlOffsetState *state = objc_getAssociatedObject(control, &RNCRefreshControlOffsetKey);
97
+ if (state == nil) return;
98
+ [state->_offsets removeObjectForKey:owner];
99
+ [state apply];
100
+ if (state->_offsets.count == 0) {
101
+ [control.layer removeObserver:state forKeyPath:@"sublayerTransform" context:RNCRefreshControlLayerContext];
102
+ [control.layer removeObserver:state forKeyPath:@"masksToBounds" context:RNCRefreshControlLayerContext];
103
+ state->_observing = NO;
104
+ objc_setAssociatedObject(control, &RNCRefreshControlOffsetKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
105
+ }
106
+ }
107
+
108
+ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
109
+ change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context
110
+ {
111
+ if (context == RNCRefreshControlLayerContext) {
112
+ if (_applying) return;
113
+ if ([keyPath isEqualToString:@"sublayerTransform"]) {
114
+ CATransform3D next = [change[NSKeyValueChangeNewKey] CATransform3DValue];
115
+ if (CATransform3DEqualToTransform(next, _appliedTransform)) return;
116
+ _baselineTransform = next;
117
+ }
118
+ [self apply];
119
+ return;
120
+ }
121
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
122
+ }
123
+
124
+ - (void)dealloc
125
+ {
126
+ if (_observing && _control != nil) {
127
+ [_control.layer removeObserver:self forKeyPath:@"sublayerTransform" context:RNCRefreshControlLayerContext];
128
+ [_control.layer removeObserver:self forKeyPath:@"masksToBounds" context:RNCRefreshControlLayerContext];
129
+ }
130
+ }
131
+ @end
21
132
  static NSString *const RNCCollapsiblePagerNativeScrollerIDPrefix =
22
133
  @"rnc-collapsible-pager-native-scroller:";
23
134
 
@@ -973,6 +1084,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
973
1084
  - (void)preparePageForHorizontalTransitionAtIndex:(NSInteger)pageIndex;
974
1085
  - (void)updateSharedHeaderPressCancellationGesture;
975
1086
  - (void)detachSharedHeaderPressCancellationGesture;
1087
+ - (void)updateRefreshControlForScrollView:(UIScrollView *)scrollView;
1088
+ - (void)releaseRefreshControlForScrollView:(UIScrollView *)scrollView;
976
1089
  - (void)sharedHeaderPressCancellationGestureChanged:(UIPanGestureRecognizer *)recognizer;
977
1090
  - (void)outerPagerGestureChanged:(UIPanGestureRecognizer *)recognizer;
978
1091
  - (void)verticalPagerGuardGestureChanged:(UIPanGestureRecognizer *)recognizer;
@@ -1023,6 +1136,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
1023
1136
  NSString *_retainedPages;
1024
1137
  NSMutableDictionary<NSString *, NSNumber *> *_pageOffsets;
1025
1138
  NSMapTable<UIScrollView *, NSValue *> *_originalInsets;
1139
+ NSMapTable<UIScrollView *, UIRefreshControl *> *_managedRefreshControls;
1140
+ NSHashTable<UIScrollView *> *_refreshControlObservedScrollViews;
1026
1141
  NSMapTable<UIScrollView *, NSNumber *> *_appliedTopInsets;
1027
1142
  NSMapTable<UIScrollView *, NSValue *> *_originalIndicatorInsets;
1028
1143
  NSMapTable<UIScrollView *, NSNumber *> *_originalAlwaysBounceVertical;
@@ -1066,6 +1181,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
1066
1181
  _pageOffsets = [NSMutableDictionary new];
1067
1182
  _releasedPageScrollStates = [NSMutableDictionary new];
1068
1183
  _originalInsets = [NSMapTable weakToStrongObjectsMapTable];
1184
+ _managedRefreshControls = [NSMapTable weakToWeakObjectsMapTable];
1185
+ _refreshControlObservedScrollViews = [NSHashTable weakObjectsHashTable];
1069
1186
  _appliedTopInsets = [NSMapTable weakToStrongObjectsMapTable];
1070
1187
  _originalIndicatorInsets = [NSMapTable weakToStrongObjectsMapTable];
1071
1188
  _originalAlwaysBounceVertical = [NSMapTable weakToStrongObjectsMapTable];
@@ -1201,6 +1318,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
1201
1318
 
1202
1319
  - (void)dealloc
1203
1320
  {
1321
+ for (UIScrollView *scrollView in _refreshControlObservedScrollViews.allObjects) {
1322
+ [self releaseRefreshControlForScrollView:scrollView];
1323
+ }
1204
1324
  [self detachSharedHeaderPressCancellationGesture];
1205
1325
  }
1206
1326
 
@@ -1739,6 +1859,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
1739
1859
  [self detachSharedHeaderPressCancellationGesture];
1740
1860
  [self detachScrollObserver];
1741
1861
  [self restoreSharedHeadersToContainer];
1862
+ for (UIScrollView *scrollView in _refreshControlObservedScrollViews.allObjects) {
1863
+ [self releaseRefreshControlForScrollView:scrollView];
1864
+ }
1742
1865
  for (UIScrollView *scrollView in _originalInsets.keyEnumerator) {
1743
1866
  NSValue *value = [_originalInsets objectForKey:scrollView];
1744
1867
  if (value != nil) {
@@ -2428,10 +2551,44 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
2428
2551
  }
2429
2552
  }
2430
2553
 
2554
+ - (void)updateRefreshControlForScrollView:(UIScrollView *)scrollView
2555
+ {
2556
+ if (!_nativeSmoothHeaderScrollEnabled || _isBeingRecycled) {
2557
+ [self releaseRefreshControlForScrollView:scrollView];
2558
+ return;
2559
+ }
2560
+ if (![_refreshControlObservedScrollViews containsObject:scrollView]) {
2561
+ [_refreshControlObservedScrollViews addObject:scrollView];
2562
+ [scrollView addObserver:self forKeyPath:@"refreshControl"
2563
+ options:NSKeyValueObservingOptionNew
2564
+ context:RNCScrollRefreshControlContext];
2565
+ }
2566
+ UIRefreshControl *previous = [_managedRefreshControls objectForKey:scrollView];
2567
+ UIRefreshControl *current = scrollView.refreshControl;
2568
+ if (previous != current) {
2569
+ [RNCRefreshControlOffsetState removeControl:previous owner:self];
2570
+ [_managedRefreshControls removeObjectForKey:scrollView];
2571
+ if (current != nil) [_managedRefreshControls setObject:current forKey:scrollView];
2572
+ }
2573
+ [RNCRefreshControlOffsetState setOffset:_headerHeight + _stickyHeaderHeight
2574
+ forControl:current owner:self];
2575
+ }
2576
+
2577
+ - (void)releaseRefreshControlForScrollView:(UIScrollView *)scrollView
2578
+ {
2579
+ if ([_refreshControlObservedScrollViews containsObject:scrollView]) {
2580
+ [scrollView removeObserver:self forKeyPath:@"refreshControl" context:RNCScrollRefreshControlContext];
2581
+ [_refreshControlObservedScrollViews removeObject:scrollView];
2582
+ }
2583
+ [RNCRefreshControlOffsetState removeControl:[_managedRefreshControls objectForKey:scrollView] owner:self];
2584
+ [_managedRefreshControls removeObjectForKey:scrollView];
2585
+ }
2586
+
2431
2587
  // OneKey patch: release observers, headers and saved state while the scroll view
2432
2588
  // still represents the old list, before Fabric applies its next owner's props.
2433
2589
  - (void)restoreScrollViewState:(UIScrollView *)scrollView resetSavedOffset:(BOOL)resetSavedOffset
2434
2590
  {
2591
+ [self releaseRefreshControlForScrollView:scrollView];
2435
2592
  if (scrollView == _observedScrollView) {
2436
2593
  [self detachScrollObserver];
2437
2594
  if (resetSavedOffset) _pageOffsets[self.currentPageKey] = @(_headerOffset);
@@ -2548,6 +2705,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
2548
2705
  // This host already includes the fixed header and safe area in its frame.
2549
2706
  // UIKit automatic adjustment would add them a second time to short pages.
2550
2707
  scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
2708
+ [self updateRefreshControlForScrollView:scrollView];
2551
2709
  CGFloat previousTopInset = scrollView.contentInset.top;
2552
2710
  CGFloat previousLogicalOffset = scrollView.contentOffset.y + previousTopInset;
2553
2711
  NSNumber *appliedTop = [_appliedTopInsets objectForKey:scrollView];
@@ -2636,6 +2794,10 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
2636
2794
  change:(NSDictionary<NSKeyValueChangeKey,id> *)change
2637
2795
  context:(void *)context
2638
2796
  {
2797
+ if (context == RNCScrollRefreshControlContext) {
2798
+ [self updateRefreshControlForScrollView:(UIScrollView *)object];
2799
+ return;
2800
+ }
2639
2801
  if (context == RNCCollapsiblePagerContentOffsetContext && object == _observedScrollView) {
2640
2802
  if ([keyPath isEqualToString:@"contentSize"]) {
2641
2803
  NSValue *oldSize = change[NSKeyValueChangeOldKey];
@@ -0,0 +1,180 @@
1
+ #import <XCTest/XCTest.h>
2
+ #import "RNCCollapsiblePagerViewComponentView.h"
3
+
4
+ @interface RNCCollapsiblePagerViewComponentView (RefreshControlTesting)
5
+ - (void)updateRefreshControlForScrollView:(UIScrollView *)scrollView;
6
+ - (void)releaseRefreshControlForScrollView:(UIScrollView *)scrollView;
7
+ @end
8
+
9
+ @interface RNCCollapsiblePagerRefreshControlTests : XCTestCase
10
+ @end
11
+
12
+ @implementation RNCCollapsiblePagerRefreshControlTests
13
+
14
+ - (RNCCollapsiblePagerViewComponentView *)pagerWithHeader:(CGFloat)height
15
+ {
16
+ RNCCollapsiblePagerViewComponentView *pager = [RNCCollapsiblePagerViewComponentView new];
17
+ [pager setValue:@YES forKey:@"nativeSmoothHeaderScrollEnabled"];
18
+ [pager setValue:@(height) forKey:@"headerHeight"];
19
+ [pager setValue:@44 forKey:@"stickyHeaderHeight"];
20
+ return pager;
21
+ }
22
+
23
+ - (void)testLateControlAndCallerOffsetUpdatesPreserveBaseline
24
+ {
25
+ RNCCollapsiblePagerViewComponentView *pager = [self pagerWithHeader:64];
26
+ UIScrollView *scroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 600)];
27
+ [pager updateRefreshControlForScrollView:scroll];
28
+ UIRefreshControl *control = [UIRefreshControl new];
29
+ control.bounds = CGRectMake(3, -12, 320, 60);
30
+ scroll.refreshControl = control;
31
+ XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, -108, 0.001);
32
+ XCTAssertEqualWithAccuracy(control.bounds.origin.y, -12, 0.001);
33
+ [pager updateRefreshControlForScrollView:scroll];
34
+ XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, -108, 0.001);
35
+ XCTAssertEqualWithAccuracy(control.bounds.origin.y, -12, 0.001);
36
+ control.layer.sublayerTransform = control.layer.sublayerTransform;
37
+ // Also cover an explicit no-op KVO notification, even if CALayer's setter
38
+ // suppresses notifications for equal transforms on the current OS.
39
+ [control.layer willChangeValueForKey:@"sublayerTransform"];
40
+ [control.layer didChangeValueForKey:@"sublayerTransform"];
41
+ XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, -108, 0.001);
42
+
43
+ // Fabric's progressViewOffset setter replaces bounds.origin.y directly.
44
+ control.bounds = CGRectMake(3, -20, 320, 60);
45
+ XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, -108, 0.001);
46
+ XCTAssertEqualWithAccuracy(control.bounds.origin.y, -20, 0.001);
47
+ [pager setValue:@100 forKey:@"headerHeight"];
48
+ [pager updateRefreshControlForScrollView:scroll];
49
+ XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, -144, 0.001);
50
+ XCTAssertEqualWithAccuracy(control.bounds.origin.y, -20, 0.001);
51
+ [pager releaseRefreshControlForScrollView:scroll];
52
+ XCTAssertEqualWithAccuracy(control.bounds.origin.y, -20, 0.001);
53
+ XCTAssertEqualWithAccuracy(control.bounds.origin.x, 3, 0.001);
54
+ }
55
+
56
+ - (void)testUIKitSizeChangesAndRefreshCycleDoNotAccumulateOffset
57
+ {
58
+ RNCCollapsiblePagerViewComponentView *pager = [self pagerWithHeader:64];
59
+ UIScrollView *scroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 600)];
60
+ // UIKit ignores offscreen beginRefreshing calls. Exercise the actual refresh
61
+ // lifecycle in a visible window rather than weakening the state assertion.
62
+ UIWindow *window = [[UIWindow alloc] initWithFrame:scroll.frame];
63
+ UIViewController *controller = [UIViewController new];
64
+ window.rootViewController = controller;
65
+ scroll.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
66
+ [controller.view addSubview:scroll];
67
+ window.hidden = NO;
68
+ [self addTeardownBlock:^{ window.hidden = YES; }];
69
+ UIRefreshControl *control = [UIRefreshControl new];
70
+ scroll.refreshControl = control;
71
+ [window layoutIfNeeded];
72
+ [scroll layoutIfNeeded];
73
+ XCTAssertEqual(control.window, window);
74
+ [pager updateRefreshControlForScrollView:scroll];
75
+ CGRect bounds = control.bounds;
76
+ bounds.size = CGSizeMake(320, 60);
77
+ control.bounds = bounds;
78
+ XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, -108, 0.001);
79
+ XCTAssertEqualWithAccuracy(control.bounds.origin.y, 0, 0.001);
80
+ [control beginRefreshing];
81
+ [control layoutIfNeeded];
82
+ [pager updateRefreshControlForScrollView:scroll];
83
+ XCTAssertTrue(control.isRefreshing);
84
+ XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, -108, 0.001);
85
+ XCTAssertEqualWithAccuracy(control.bounds.origin.y, 0, 0.001);
86
+ [control endRefreshing];
87
+ [control layoutIfNeeded];
88
+ XCTAssertFalse(control.isRefreshing);
89
+ XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, -108, 0.001);
90
+ XCTAssertEqualWithAccuracy(control.bounds.origin.y, 0, 0.001);
91
+ [pager releaseRefreshControlForScrollView:scroll];
92
+ XCTAssertEqualWithAccuracy(control.bounds.origin.y, 0, 0.001);
93
+ XCTAssertTrue(CATransform3DIsIdentity(control.layer.sublayerTransform));
94
+ }
95
+
96
+ - (void)testReplacementAndNestedOwnerReleaseRestoreOnlyTheirContributions
97
+ {
98
+ RNCCollapsiblePagerViewComponentView *first = [self pagerWithHeader:64];
99
+ RNCCollapsiblePagerViewComponentView *second = [self pagerWithHeader:20];
100
+ UICollectionView *scroll = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, 320, 600)
101
+ collectionViewLayout:[UICollectionViewFlowLayout new]];
102
+ UIRefreshControl *original = [UIRefreshControl new];
103
+ scroll.refreshControl = original;
104
+ [first updateRefreshControlForScrollView:scroll];
105
+ [second updateRefreshControlForScrollView:scroll];
106
+ XCTAssertEqualWithAccuracy(original.layer.sublayerTransform.m42, -172, 0.001);
107
+ [first releaseRefreshControlForScrollView:scroll];
108
+ XCTAssertEqualWithAccuracy(original.layer.sublayerTransform.m42, -64, 0.001);
109
+
110
+ UIRefreshControl *replacement = [UIRefreshControl new];
111
+ replacement.bounds = CGRectMake(0, -8, 320, 60);
112
+ scroll.refreshControl = replacement;
113
+ XCTAssertTrue(CATransform3DIsIdentity(original.layer.sublayerTransform));
114
+ XCTAssertEqualWithAccuracy(replacement.layer.sublayerTransform.m42, -64, 0.001);
115
+ XCTAssertEqualWithAccuracy(replacement.bounds.origin.y, -8, 0.001);
116
+ [second prepareForRecycle];
117
+ XCTAssertEqualWithAccuracy(replacement.bounds.origin.y, -8, 0.001);
118
+ XCTAssertTrue(CATransform3DIsIdentity(replacement.layer.sublayerTransform));
119
+ scroll.refreshControl = nil;
120
+ }
121
+
122
+ - (void)testPagerDestructionRestoresControl
123
+ {
124
+ UIScrollView *scroll = [UIScrollView new];
125
+ UIRefreshControl *control = [UIRefreshControl new];
126
+ scroll.refreshControl = control;
127
+ @autoreleasepool {
128
+ RNCCollapsiblePagerViewComponentView *pager = [self pagerWithHeader:64];
129
+ [pager updateRefreshControlForScrollView:scroll];
130
+ XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, -108, 0.001);
131
+ XCTAssertEqualWithAccuracy(control.bounds.origin.y, 0, 0.001);
132
+ }
133
+ XCTAssertEqualWithAccuracy(control.bounds.origin.y, 0, 0.001);
134
+ XCTAssertTrue(CATransform3DIsIdentity(control.layer.sublayerTransform));
135
+ }
136
+
137
+ - (void)testCallerOffsetCollisionDoesNotAffectVisualContribution
138
+ {
139
+ RNCCollapsiblePagerViewComponentView *pager = [self pagerWithHeader:56];
140
+ UIScrollView *scroll = [UIScrollView new];
141
+ UIRefreshControl *control = [UIRefreshControl new];
142
+ scroll.refreshControl = control;
143
+ control.bounds = CGRectMake(0, -200, 320, 60);
144
+ [pager updateRefreshControlForScrollView:scroll];
145
+ control.bounds = CGRectMake(0, -100, 320, 60);
146
+ XCTAssertEqualWithAccuracy(control.bounds.origin.y, -100, 0.001);
147
+ XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, -100, 0.001);
148
+ [pager releaseRefreshControlForScrollView:scroll];
149
+ XCTAssertEqualWithAccuracy(control.bounds.origin.y, -100, 0.001);
150
+ XCTAssertTrue(CATransform3DIsIdentity(control.layer.sublayerTransform));
151
+ }
152
+
153
+ - (void)testNestedOwnerDestructionPreservesBaselineAndRestoresClipping
154
+ {
155
+ RNCCollapsiblePagerViewComponentView *second = [self pagerWithHeader:20];
156
+ UIScrollView *scroll = [UIScrollView new];
157
+ UIRefreshControl *control = [UIRefreshControl new];
158
+ scroll.refreshControl = control;
159
+ CATransform3D baseline = CATransform3DMakeTranslation(7, 13, 0);
160
+ control.layer.sublayerTransform = baseline;
161
+ control.layer.masksToBounds = YES;
162
+ @autoreleasepool {
163
+ RNCCollapsiblePagerViewComponentView *first = [self pagerWithHeader:64];
164
+ [first updateRefreshControlForScrollView:scroll];
165
+ [second updateRefreshControlForScrollView:scroll];
166
+ XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, 13 - 172, 0.001);
167
+ XCTAssertFalse(control.layer.masksToBounds);
168
+ }
169
+ XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, 13 - 64, 0.001);
170
+ // A UIKit layer reset must not discard the remaining visual contribution.
171
+ control.layer.sublayerTransform = baseline;
172
+ control.layer.masksToBounds = YES;
173
+ XCTAssertEqualWithAccuracy(control.layer.sublayerTransform.m42, 13 - 64, 0.001);
174
+ XCTAssertFalse(control.layer.masksToBounds);
175
+ [second releaseRefreshControlForScrollView:scroll];
176
+ XCTAssertTrue(CATransform3DEqualToTransform(control.layer.sublayerTransform, baseline));
177
+ XCTAssertTrue(control.layer.masksToBounds);
178
+ }
179
+
180
+ @end
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/react-native-pager-view",
3
- "version": "3.0.157-alpha.255",
3
+ "version": "3.0.157-alpha.256",
4
4
  "description": "React Native wrapper for Android and iOS ViewPager",
5
5
  "source": "./src/index.tsx",
6
6
  "main": "./lib/module/index.js",