@onekeyfe/react-native-pager-view 3.0.113 → 3.0.114
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/README.md +0 -40
- package/android/src/main/java/com/reactnativepagerview/PagerViewViewPackage.kt +1 -1
- package/lib/module/index.js +1 -2
- package/lib/module/usePagerView.js +1 -1
- package/lib/typescript/src/index.d.ts +4 -5
- package/package.json +2 -5
- package/src/index.tsx +4 -5
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerHost.kt +0 -503
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerViewManager.kt +0 -215
- package/android/src/main/java/com/reactnativepagerview/event/CollapsibleStateChangedEvent.kt +0 -39
- package/ios/RNCCollapsiblePagerViewComponentView.h +0 -9
- package/ios/RNCCollapsiblePagerViewComponentView.mm +0 -738
- package/lib/module/CollapsiblePagerView.js +0 -152
- package/lib/module/CollapsiblePagerView.web.js +0 -364
- package/lib/module/CollapsiblePagerViewNativeComponent.ts +0 -85
- package/lib/module/PagerView.web.js +0 -177
- package/lib/module/index.web.js +0 -6
- package/lib/typescript/src/CollapsiblePagerView.d.ts +0 -58
- package/lib/typescript/src/CollapsiblePagerView.web.d.ts +0 -93
- package/lib/typescript/src/CollapsiblePagerViewNativeComponent.d.ts +0 -50
- package/lib/typescript/src/PagerView.web.d.ts +0 -55
- package/lib/typescript/src/index.web.d.ts +0 -5
- package/src/CollapsiblePagerView.tsx +0 -258
- package/src/CollapsiblePagerView.web.tsx +0 -559
- package/src/CollapsiblePagerViewNativeComponent.ts +0 -85
- package/src/PagerView.web.tsx +0 -280
- package/src/index.web.tsx +0 -12
|
@@ -1,738 +0,0 @@
|
|
|
1
|
-
#import "RNCCollapsiblePagerViewComponentView.h"
|
|
2
|
-
|
|
3
|
-
#import <react/renderer/components/pagerview/ComponentDescriptors.h>
|
|
4
|
-
#import <react/renderer/components/pagerview/EventEmitters.h>
|
|
5
|
-
#import <react/renderer/components/pagerview/Props.h>
|
|
6
|
-
#import <react/renderer/components/pagerview/RCTComponentViewHelpers.h>
|
|
7
|
-
|
|
8
|
-
#import "React/RCTConversions.h"
|
|
9
|
-
|
|
10
|
-
using namespace facebook::react;
|
|
11
|
-
|
|
12
|
-
static void *RNCCollapsiblePagerContentOffsetContext = &RNCCollapsiblePagerContentOffsetContext;
|
|
13
|
-
|
|
14
|
-
@interface RNCCollapsiblePagerViewComponentView () <
|
|
15
|
-
RCTRNCCollapsiblePagerViewViewProtocol,
|
|
16
|
-
UIPageViewControllerDataSource,
|
|
17
|
-
UIPageViewControllerDelegate,
|
|
18
|
-
UIScrollViewDelegate
|
|
19
|
-
>
|
|
20
|
-
@end
|
|
21
|
-
|
|
22
|
-
@implementation RNCCollapsiblePagerViewComponentView {
|
|
23
|
-
UIView *_containerView;
|
|
24
|
-
UIPageViewController *_pageViewController;
|
|
25
|
-
UIScrollView *_pagerScrollView;
|
|
26
|
-
NSMutableArray<UIView<RCTComponentViewProtocol> *> *_logicalChildren;
|
|
27
|
-
NSMutableArray<UIViewController *> *_pageControllers;
|
|
28
|
-
UIView *_headerView;
|
|
29
|
-
UIView *_stickyHeaderView;
|
|
30
|
-
NSInteger _currentIndex;
|
|
31
|
-
NSInteger _destinationIndex;
|
|
32
|
-
NSInteger _pendingInitialPage;
|
|
33
|
-
NSInteger _headerHeight;
|
|
34
|
-
NSInteger _stickyHeaderHeight;
|
|
35
|
-
CGFloat _headerOffset;
|
|
36
|
-
BOOL _scrollEnabled;
|
|
37
|
-
BOOL _transitioning;
|
|
38
|
-
BOOL _hasAppliedInitialPage;
|
|
39
|
-
NSString *_layoutDirection;
|
|
40
|
-
NSArray<NSString *> *_pageKeys;
|
|
41
|
-
NSString *_retainedPages;
|
|
42
|
-
NSMutableDictionary<NSString *, NSNumber *> *_pageOffsets;
|
|
43
|
-
NSMapTable<UIScrollView *, NSValue *> *_originalInsets;
|
|
44
|
-
NSMapTable<UIScrollView *, NSValue *> *_originalIndicatorInsets;
|
|
45
|
-
NSMapTable<UIScrollView *, NSNumber *> *_originalAlwaysBounceVertical;
|
|
46
|
-
NSMapTable<UIScrollView *, NSNumber *> *_originalInsetAdjustmentBehavior;
|
|
47
|
-
__weak UIScrollView *_observedScrollView;
|
|
48
|
-
BOOL _observingContentOffset;
|
|
49
|
-
BOOL _isBeingRecycled;
|
|
50
|
-
NSUInteger _generation;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
+ (void)load
|
|
54
|
-
{
|
|
55
|
-
[super load];
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
- (instancetype)initWithFrame:(CGRect)frame
|
|
59
|
-
{
|
|
60
|
-
if (self = [super initWithFrame:frame]) {
|
|
61
|
-
static const auto defaultProps = std::make_shared<const RNCCollapsiblePagerViewProps>();
|
|
62
|
-
_props = defaultProps;
|
|
63
|
-
_logicalChildren = [NSMutableArray new];
|
|
64
|
-
_pageControllers = [NSMutableArray new];
|
|
65
|
-
_pageOffsets = [NSMutableDictionary new];
|
|
66
|
-
_originalInsets = [NSMapTable weakToStrongObjectsMapTable];
|
|
67
|
-
_originalIndicatorInsets = [NSMapTable weakToStrongObjectsMapTable];
|
|
68
|
-
_originalAlwaysBounceVertical = [NSMapTable weakToStrongObjectsMapTable];
|
|
69
|
-
_originalInsetAdjustmentBehavior = [NSMapTable weakToStrongObjectsMapTable];
|
|
70
|
-
_pageKeys = @[];
|
|
71
|
-
_retainedPages = @"[]";
|
|
72
|
-
_currentIndex = 0;
|
|
73
|
-
_destinationIndex = 0;
|
|
74
|
-
_pendingInitialPage = 0;
|
|
75
|
-
_scrollEnabled = YES;
|
|
76
|
-
_hasAppliedInitialPage = NO;
|
|
77
|
-
_layoutDirection = @"ltr";
|
|
78
|
-
|
|
79
|
-
_containerView = [UIView new];
|
|
80
|
-
_containerView.clipsToBounds = YES;
|
|
81
|
-
self.contentView = _containerView;
|
|
82
|
-
|
|
83
|
-
[self initializePageViewController];
|
|
84
|
-
}
|
|
85
|
-
return self;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
- (void)willMoveToSuperview:(UIView *)newSuperview
|
|
89
|
-
{
|
|
90
|
-
[super willMoveToSuperview:newSuperview];
|
|
91
|
-
if (newSuperview != nil && _pageViewController == nil) {
|
|
92
|
-
self.contentView = _containerView;
|
|
93
|
-
[self initializePageViewController];
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
- (void)initializePageViewController
|
|
98
|
-
{
|
|
99
|
-
_pageViewController = [[UIPageViewController alloc]
|
|
100
|
-
initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll
|
|
101
|
-
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
|
|
102
|
-
options:nil];
|
|
103
|
-
_pageViewController.dataSource = self;
|
|
104
|
-
_pageViewController.delegate = self;
|
|
105
|
-
[_containerView addSubview:_pageViewController.view];
|
|
106
|
-
|
|
107
|
-
for (UIView *subview in _pageViewController.view.subviews) {
|
|
108
|
-
if ([subview isKindOfClass:UIScrollView.class]) {
|
|
109
|
-
_pagerScrollView = (UIScrollView *)subview;
|
|
110
|
-
_pagerScrollView.delegate = self;
|
|
111
|
-
_pagerScrollView.delaysContentTouches = NO;
|
|
112
|
-
_pagerScrollView.scrollEnabled = _scrollEnabled;
|
|
113
|
-
break;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
#pragma mark - React mounting
|
|
119
|
-
|
|
120
|
-
- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView
|
|
121
|
-
index:(NSInteger)index
|
|
122
|
-
{
|
|
123
|
-
if (_isBeingRecycled) return;
|
|
124
|
-
NSInteger safeIndex = MIN(MAX(index, 0), _logicalChildren.count);
|
|
125
|
-
[_logicalChildren insertObject:childComponentView atIndex:safeIndex];
|
|
126
|
-
[self rebuildSlots];
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
- (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView
|
|
130
|
-
index:(NSInteger)index
|
|
131
|
-
{
|
|
132
|
-
if (_isBeingRecycled) return;
|
|
133
|
-
NSUInteger existingIndex = [_logicalChildren indexOfObjectIdenticalTo:childComponentView];
|
|
134
|
-
if (existingIndex == NSNotFound) return;
|
|
135
|
-
[childComponentView removeFromSuperview];
|
|
136
|
-
[_logicalChildren removeObjectAtIndex:existingIndex];
|
|
137
|
-
[self rebuildSlots];
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
- (void)rebuildSlots
|
|
141
|
-
{
|
|
142
|
-
[self detachScrollObserver];
|
|
143
|
-
[_headerView removeFromSuperview];
|
|
144
|
-
[_stickyHeaderView removeFromSuperview];
|
|
145
|
-
_headerView = nil;
|
|
146
|
-
_stickyHeaderView = nil;
|
|
147
|
-
[_pageControllers removeAllObjects];
|
|
148
|
-
|
|
149
|
-
if (_logicalChildren.count > 0) {
|
|
150
|
-
_headerView = _logicalChildren[0];
|
|
151
|
-
[_containerView addSubview:_headerView];
|
|
152
|
-
}
|
|
153
|
-
if (_logicalChildren.count > 1) {
|
|
154
|
-
_stickyHeaderView = _logicalChildren[1];
|
|
155
|
-
[_containerView addSubview:_stickyHeaderView];
|
|
156
|
-
}
|
|
157
|
-
for (NSInteger index = 2; index < _logicalChildren.count; index++) {
|
|
158
|
-
UIView *page = _logicalChildren[index];
|
|
159
|
-
UIViewController *controller = [UIViewController new];
|
|
160
|
-
[controller.view addSubview:page];
|
|
161
|
-
[_pageControllers addObject:controller];
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (_pageControllers.count > 0) {
|
|
165
|
-
if (!_hasAppliedInitialPage &&
|
|
166
|
-
_pendingInitialPage >= 0 &&
|
|
167
|
-
_pendingInitialPage < _pageControllers.count) {
|
|
168
|
-
_currentIndex = _pendingInitialPage;
|
|
169
|
-
_hasAppliedInitialPage = YES;
|
|
170
|
-
} else {
|
|
171
|
-
_currentIndex = MIN(MAX(_currentIndex, 0), _pageControllers.count - 1);
|
|
172
|
-
}
|
|
173
|
-
UIViewController *controller = _pageControllers[_currentIndex];
|
|
174
|
-
[_pageViewController setViewControllers:@[controller]
|
|
175
|
-
direction:UIPageViewControllerNavigationDirectionForward
|
|
176
|
-
animated:NO
|
|
177
|
-
completion:nil];
|
|
178
|
-
}
|
|
179
|
-
if (_headerView != nil) [_containerView bringSubviewToFront:_headerView];
|
|
180
|
-
if (_stickyHeaderView != nil) [_containerView bringSubviewToFront:_stickyHeaderView];
|
|
181
|
-
[self setNeedsLayout];
|
|
182
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
183
|
-
if (!self->_isBeingRecycled) {
|
|
184
|
-
[self prepareAdjacentPageInsets];
|
|
185
|
-
[self attachScrollObserverForCurrentPage];
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
- (void)layoutSubviews
|
|
191
|
-
{
|
|
192
|
-
[super layoutSubviews];
|
|
193
|
-
_containerView.frame = self.bounds;
|
|
194
|
-
_pageViewController.view.frame = _containerView.bounds;
|
|
195
|
-
// UIKit frame assignment is undefined while a view has a non-identity transform.
|
|
196
|
-
// Lay out the original slots, then restore the shared collapse translation.
|
|
197
|
-
_headerView.transform = CGAffineTransformIdentity;
|
|
198
|
-
_stickyHeaderView.transform = CGAffineTransformIdentity;
|
|
199
|
-
_headerView.frame = CGRectMake(0, 0, self.bounds.size.width, _headerHeight);
|
|
200
|
-
_stickyHeaderView.frame = CGRectMake(
|
|
201
|
-
0,
|
|
202
|
-
_headerHeight,
|
|
203
|
-
self.bounds.size.width,
|
|
204
|
-
_stickyHeaderHeight
|
|
205
|
-
);
|
|
206
|
-
for (UIViewController *controller in _pageControllers) {
|
|
207
|
-
controller.view.frame = _pageViewController.view.bounds;
|
|
208
|
-
controller.view.subviews.firstObject.frame = controller.view.bounds;
|
|
209
|
-
}
|
|
210
|
-
[self applyHeaderOffset];
|
|
211
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
212
|
-
if (!self->_isBeingRecycled) {
|
|
213
|
-
[self prepareAdjacentPageInsets];
|
|
214
|
-
[self attachScrollObserverForCurrentPage];
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
- (void)prepareForRecycle
|
|
220
|
-
{
|
|
221
|
-
_isBeingRecycled = YES;
|
|
222
|
-
_generation++;
|
|
223
|
-
[self detachScrollObserver];
|
|
224
|
-
for (UIScrollView *scrollView in _originalInsets.keyEnumerator) {
|
|
225
|
-
NSValue *value = [_originalInsets objectForKey:scrollView];
|
|
226
|
-
if (value != nil) scrollView.contentInset = value.UIEdgeInsetsValue;
|
|
227
|
-
NSValue *indicatorValue = [_originalIndicatorInsets objectForKey:scrollView];
|
|
228
|
-
if (indicatorValue != nil) {
|
|
229
|
-
scrollView.verticalScrollIndicatorInsets = indicatorValue.UIEdgeInsetsValue;
|
|
230
|
-
}
|
|
231
|
-
NSNumber *alwaysBounce = [_originalAlwaysBounceVertical objectForKey:scrollView];
|
|
232
|
-
if (alwaysBounce != nil) scrollView.alwaysBounceVertical = alwaysBounce.boolValue;
|
|
233
|
-
NSNumber *adjustment = [_originalInsetAdjustmentBehavior objectForKey:scrollView];
|
|
234
|
-
if (adjustment != nil) {
|
|
235
|
-
scrollView.contentInsetAdjustmentBehavior = (UIScrollViewContentInsetAdjustmentBehavior)adjustment.integerValue;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
[_originalInsets removeAllObjects];
|
|
239
|
-
[_originalIndicatorInsets removeAllObjects];
|
|
240
|
-
[_originalAlwaysBounceVertical removeAllObjects];
|
|
241
|
-
[_originalInsetAdjustmentBehavior removeAllObjects];
|
|
242
|
-
[_headerView removeFromSuperview];
|
|
243
|
-
[_stickyHeaderView removeFromSuperview];
|
|
244
|
-
[_logicalChildren removeAllObjects];
|
|
245
|
-
[_pageControllers removeAllObjects];
|
|
246
|
-
_headerView = nil;
|
|
247
|
-
_stickyHeaderView = nil;
|
|
248
|
-
_pageViewController.dataSource = nil;
|
|
249
|
-
_pageViewController.delegate = nil;
|
|
250
|
-
_pagerScrollView.delegate = nil;
|
|
251
|
-
[_pageViewController.view removeFromSuperview];
|
|
252
|
-
_pageViewController = nil;
|
|
253
|
-
_pagerScrollView = nil;
|
|
254
|
-
[super prepareForRecycle];
|
|
255
|
-
[_pageOffsets removeAllObjects];
|
|
256
|
-
_pageKeys = @[];
|
|
257
|
-
_retainedPages = @"[]";
|
|
258
|
-
_currentIndex = 0;
|
|
259
|
-
_destinationIndex = 0;
|
|
260
|
-
_pendingInitialPage = 0;
|
|
261
|
-
_headerHeight = 0;
|
|
262
|
-
_stickyHeaderHeight = 0;
|
|
263
|
-
_headerOffset = 0;
|
|
264
|
-
_scrollEnabled = YES;
|
|
265
|
-
_transitioning = NO;
|
|
266
|
-
_hasAppliedInitialPage = NO;
|
|
267
|
-
_layoutDirection = @"ltr";
|
|
268
|
-
_isBeingRecycled = NO;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
#pragma mark - Props
|
|
272
|
-
|
|
273
|
-
- (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &)oldProps
|
|
274
|
-
{
|
|
275
|
-
const auto &oldViewProps = *std::static_pointer_cast<const RNCCollapsiblePagerViewProps>(_props);
|
|
276
|
-
const auto &newViewProps = *std::static_pointer_cast<const RNCCollapsiblePagerViewProps>(props);
|
|
277
|
-
|
|
278
|
-
if (oldViewProps.initialPage != newViewProps.initialPage) {
|
|
279
|
-
_pendingInitialPage = MAX(0, newViewProps.initialPage);
|
|
280
|
-
_hasAppliedInitialPage = NO;
|
|
281
|
-
}
|
|
282
|
-
if (oldViewProps.scrollEnabled != newViewProps.scrollEnabled) {
|
|
283
|
-
_scrollEnabled = newViewProps.scrollEnabled;
|
|
284
|
-
_pagerScrollView.scrollEnabled = _scrollEnabled;
|
|
285
|
-
}
|
|
286
|
-
if (oldViewProps.layoutDirection != newViewProps.layoutDirection) {
|
|
287
|
-
_layoutDirection = RCTNSStringFromString(toString(newViewProps.layoutDirection));
|
|
288
|
-
}
|
|
289
|
-
BOOL insetsChanged = NO;
|
|
290
|
-
if (oldViewProps.headerHeight != newViewProps.headerHeight) {
|
|
291
|
-
_headerHeight = MAX(0, newViewProps.headerHeight);
|
|
292
|
-
_headerOffset = MIN(_headerOffset, _headerHeight);
|
|
293
|
-
insetsChanged = YES;
|
|
294
|
-
}
|
|
295
|
-
if (oldViewProps.stickyHeaderHeight != newViewProps.stickyHeaderHeight) {
|
|
296
|
-
_stickyHeaderHeight = MAX(0, newViewProps.stickyHeaderHeight);
|
|
297
|
-
insetsChanged = YES;
|
|
298
|
-
}
|
|
299
|
-
if (oldViewProps.pageKeys != newViewProps.pageKeys) {
|
|
300
|
-
NSString *json = RCTNSStringFromString(newViewProps.pageKeys);
|
|
301
|
-
NSData *data = [json dataUsingEncoding:NSUTF8StringEncoding];
|
|
302
|
-
id parsed = data == nil ? nil : [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
|
|
303
|
-
_pageKeys = [parsed isKindOfClass:NSArray.class] ? parsed : @[];
|
|
304
|
-
}
|
|
305
|
-
if (oldViewProps.retainedPages != newViewProps.retainedPages) {
|
|
306
|
-
_retainedPages = RCTNSStringFromString(newViewProps.retainedPages);
|
|
307
|
-
[self setNeedsLayout];
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
[super updateProps:props oldProps:oldProps];
|
|
311
|
-
if (insetsChanged) {
|
|
312
|
-
[self reapplyInsetsToObservedScrollView];
|
|
313
|
-
[self setNeedsLayout];
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
#pragma mark - Collapsible scroll coordination
|
|
318
|
-
|
|
319
|
-
- (NSString *)currentPageKey
|
|
320
|
-
{
|
|
321
|
-
return [self pageKeyForIndex:_currentIndex];
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
- (NSString *)pageKeyForIndex:(NSInteger)index
|
|
325
|
-
{
|
|
326
|
-
if (index >= 0 && index < _pageKeys.count) return _pageKeys[index];
|
|
327
|
-
return [NSString stringWithFormat:@"page-%ld", (long)index];
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
- (UIScrollView *)findVerticalScrollViewInView:(UIView *)view
|
|
331
|
-
{
|
|
332
|
-
if ([view isKindOfClass:UICollectionView.class]) {
|
|
333
|
-
UICollectionView *collectionView = (UICollectionView *)view;
|
|
334
|
-
UICollectionViewLayout *layout = collectionView.collectionViewLayout;
|
|
335
|
-
if (![layout isKindOfClass:UICollectionViewFlowLayout.class] ||
|
|
336
|
-
((UICollectionViewFlowLayout *)layout).scrollDirection == UICollectionViewScrollDirectionVertical) {
|
|
337
|
-
return collectionView;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
// Empty pages may use an RN ScrollView instead of a native row list.
|
|
341
|
-
if ([view isKindOfClass:UIScrollView.class] &&
|
|
342
|
-
![view isKindOfClass:UICollectionView.class]) {
|
|
343
|
-
UIScrollView *scrollView = (UIScrollView *)view;
|
|
344
|
-
if (!scrollView.alwaysBounceHorizontal &&
|
|
345
|
-
scrollView.contentSize.width <= scrollView.bounds.size.width + 1) {
|
|
346
|
-
return scrollView;
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
for (UIView *subview in view.subviews) {
|
|
350
|
-
UIScrollView *candidate = [self findVerticalScrollViewInView:subview];
|
|
351
|
-
if (candidate != nil) return candidate;
|
|
352
|
-
}
|
|
353
|
-
return nil;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
- (void)attachScrollObserverForCurrentPage
|
|
357
|
-
{
|
|
358
|
-
[self restoreDetachedScrollInsets];
|
|
359
|
-
if (_currentIndex < 0 || _currentIndex >= _pageControllers.count) return;
|
|
360
|
-
UIScrollView *candidate = [self findVerticalScrollViewInView:_pageControllers[_currentIndex].view];
|
|
361
|
-
if (candidate == nil || candidate == _observedScrollView) return;
|
|
362
|
-
|
|
363
|
-
BOOL replacingCurrentScrollView = _observingContentOffset;
|
|
364
|
-
[self detachScrollObserver];
|
|
365
|
-
if (replacingCurrentScrollView) {
|
|
366
|
-
// A list replaced by an empty RN view keeps collapse, not the old row offset.
|
|
367
|
-
_pageOffsets[self.currentPageKey] = @(_headerOffset);
|
|
368
|
-
}
|
|
369
|
-
_observedScrollView = candidate;
|
|
370
|
-
[self applyInsetsToScrollView:candidate pageIndex:_currentIndex restore:YES];
|
|
371
|
-
[candidate addObserver:self
|
|
372
|
-
forKeyPath:@"contentOffset"
|
|
373
|
-
options:NSKeyValueObservingOptionNew
|
|
374
|
-
context:RNCCollapsiblePagerContentOffsetContext];
|
|
375
|
-
[candidate addObserver:self
|
|
376
|
-
forKeyPath:@"contentSize"
|
|
377
|
-
options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew
|
|
378
|
-
context:RNCCollapsiblePagerContentOffsetContext];
|
|
379
|
-
_observingContentOffset = YES;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
- (void)detachScrollObserver
|
|
383
|
-
{
|
|
384
|
-
UIScrollView *scrollView = _observedScrollView;
|
|
385
|
-
if (scrollView != nil) {
|
|
386
|
-
_pageOffsets[self.currentPageKey] = @(scrollView.contentOffset.y + scrollView.contentInset.top);
|
|
387
|
-
if (_observingContentOffset) {
|
|
388
|
-
[scrollView removeObserver:self
|
|
389
|
-
forKeyPath:@"contentOffset"
|
|
390
|
-
context:RNCCollapsiblePagerContentOffsetContext];
|
|
391
|
-
[scrollView removeObserver:self
|
|
392
|
-
forKeyPath:@"contentSize"
|
|
393
|
-
context:RNCCollapsiblePagerContentOffsetContext];
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
_observingContentOffset = NO;
|
|
397
|
-
_observedScrollView = nil;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
- (void)restoreDetachedScrollInsets
|
|
401
|
-
{
|
|
402
|
-
for (UIScrollView *scrollView in _originalInsets.keyEnumerator.allObjects) {
|
|
403
|
-
BOOL belongsToPage = NO;
|
|
404
|
-
for (UIViewController *controller in _pageControllers) {
|
|
405
|
-
if ([scrollView isDescendantOfView:controller.view]) {
|
|
406
|
-
belongsToPage = YES;
|
|
407
|
-
break;
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
if (belongsToPage) continue;
|
|
411
|
-
if (scrollView == _observedScrollView) {
|
|
412
|
-
[self detachScrollObserver];
|
|
413
|
-
_pageOffsets[self.currentPageKey] = @(_headerOffset);
|
|
414
|
-
}
|
|
415
|
-
// Fabric can reuse an empty RN ScrollView in another screen. Release all
|
|
416
|
-
// pager-owned insets before that view is used as a search or dialog list.
|
|
417
|
-
CGFloat logicalOffset = scrollView.contentOffset.y + scrollView.contentInset.top;
|
|
418
|
-
UIEdgeInsets original = [_originalInsets objectForKey:scrollView].UIEdgeInsetsValue;
|
|
419
|
-
scrollView.contentInset = original;
|
|
420
|
-
scrollView.verticalScrollIndicatorInsets =
|
|
421
|
-
[_originalIndicatorInsets objectForKey:scrollView].UIEdgeInsetsValue;
|
|
422
|
-
scrollView.alwaysBounceVertical =
|
|
423
|
-
[_originalAlwaysBounceVertical objectForKey:scrollView].boolValue;
|
|
424
|
-
scrollView.contentInsetAdjustmentBehavior = (UIScrollViewContentInsetAdjustmentBehavior)
|
|
425
|
-
[_originalInsetAdjustmentBehavior objectForKey:scrollView].integerValue;
|
|
426
|
-
[scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x, logicalOffset - original.top)
|
|
427
|
-
animated:NO];
|
|
428
|
-
[_originalInsets removeObjectForKey:scrollView];
|
|
429
|
-
[_originalIndicatorInsets removeObjectForKey:scrollView];
|
|
430
|
-
[_originalAlwaysBounceVertical removeObjectForKey:scrollView];
|
|
431
|
-
[_originalInsetAdjustmentBehavior removeObjectForKey:scrollView];
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
- (void)applyInsetsToScrollView:(UIScrollView *)scrollView
|
|
436
|
-
pageIndex:(NSInteger)pageIndex
|
|
437
|
-
restore:(BOOL)restore
|
|
438
|
-
{
|
|
439
|
-
NSValue *storedOriginal = [_originalInsets objectForKey:scrollView];
|
|
440
|
-
UIEdgeInsets original = storedOriginal == nil
|
|
441
|
-
? scrollView.contentInset
|
|
442
|
-
: storedOriginal.UIEdgeInsetsValue;
|
|
443
|
-
if (storedOriginal == nil) {
|
|
444
|
-
[_originalInsets setObject:[NSValue valueWithUIEdgeInsets:original] forKey:scrollView];
|
|
445
|
-
[_originalIndicatorInsets
|
|
446
|
-
setObject:[NSValue valueWithUIEdgeInsets:scrollView.verticalScrollIndicatorInsets]
|
|
447
|
-
forKey:scrollView];
|
|
448
|
-
[_originalAlwaysBounceVertical setObject:@(scrollView.alwaysBounceVertical) forKey:scrollView];
|
|
449
|
-
[_originalInsetAdjustmentBehavior setObject:@(scrollView.contentInsetAdjustmentBehavior) forKey:scrollView];
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
// This host already includes the fixed header and safe area in its frame.
|
|
453
|
-
// UIKit automatic adjustment would add them a second time to short pages.
|
|
454
|
-
scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
|
455
|
-
CGFloat previousLogicalOffset = scrollView.contentOffset.y + scrollView.contentInset.top;
|
|
456
|
-
UIEdgeInsets next = original;
|
|
457
|
-
next.top += _headerHeight + _stickyHeaderHeight;
|
|
458
|
-
// Short pages still need enough range to collapse the header; long pages
|
|
459
|
-
// must end at the last row rather than leave a header-sized blank footer.
|
|
460
|
-
next.bottom += MAX(0, scrollView.bounds.size.height - _stickyHeaderHeight
|
|
461
|
-
- original.top - original.bottom - scrollView.contentSize.height);
|
|
462
|
-
if (UIEdgeInsetsEqualToEdgeInsets(scrollView.contentInset, next) && !restore) {
|
|
463
|
-
// UICollectionView may report contentSize during a bounce without changing
|
|
464
|
-
// the required insets. Re-setting contentOffset here interrupts that bounce.
|
|
465
|
-
if (pageIndex == _currentIndex) [self updateHeaderForScrollView:scrollView];
|
|
466
|
-
return;
|
|
467
|
-
}
|
|
468
|
-
scrollView.contentInset = next;
|
|
469
|
-
UIEdgeInsets indicatorInsets = scrollView.verticalScrollIndicatorInsets;
|
|
470
|
-
indicatorInsets.top = next.top;
|
|
471
|
-
scrollView.verticalScrollIndicatorInsets = indicatorInsets;
|
|
472
|
-
scrollView.alwaysBounceVertical = YES;
|
|
473
|
-
|
|
474
|
-
CGFloat targetOffset = previousLogicalOffset - next.top;
|
|
475
|
-
if (restore) {
|
|
476
|
-
NSNumber *saved = _pageOffsets[[self pageKeyForIndex:pageIndex]];
|
|
477
|
-
// The sticky slot can change height between pages. Cache offsets relative
|
|
478
|
-
// to content start so a round trip does not add that height difference.
|
|
479
|
-
targetOffset = (saved == nil ? _headerOffset : saved.doubleValue) - next.top;
|
|
480
|
-
targetOffset = MAX(targetOffset, -next.top + _headerOffset);
|
|
481
|
-
}
|
|
482
|
-
[scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x, targetOffset) animated:NO];
|
|
483
|
-
if (pageIndex == _currentIndex) [self updateHeaderForScrollView:scrollView];
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
- (void)reapplyInsetsToObservedScrollView
|
|
487
|
-
{
|
|
488
|
-
[self restoreDetachedScrollInsets];
|
|
489
|
-
UIScrollView *scrollView = _observedScrollView;
|
|
490
|
-
if (scrollView != nil) {
|
|
491
|
-
[self applyInsetsToScrollView:scrollView pageIndex:_currentIndex restore:NO];
|
|
492
|
-
}
|
|
493
|
-
[self prepareAdjacentPageInsets];
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
- (void)prepareAdjacentPageInsets
|
|
497
|
-
{
|
|
498
|
-
[self restoreDetachedScrollInsets];
|
|
499
|
-
NSInteger first = MAX(0, _currentIndex - 1);
|
|
500
|
-
NSInteger last = MIN((NSInteger)_pageControllers.count - 1, _currentIndex + 1);
|
|
501
|
-
if (last < first) return;
|
|
502
|
-
for (NSInteger index = first; index <= last; index++) {
|
|
503
|
-
UIScrollView *scrollView = [self findVerticalScrollViewInView:_pageControllers[index].view];
|
|
504
|
-
if (scrollView != nil && scrollView != _observedScrollView) {
|
|
505
|
-
[self applyInsetsToScrollView:scrollView pageIndex:index restore:YES];
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
- (void)observeValueForKeyPath:(NSString *)keyPath
|
|
511
|
-
ofObject:(id)object
|
|
512
|
-
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
|
|
513
|
-
context:(void *)context
|
|
514
|
-
{
|
|
515
|
-
if (context == RNCCollapsiblePagerContentOffsetContext && object == _observedScrollView) {
|
|
516
|
-
if ([keyPath isEqualToString:@"contentSize"]) {
|
|
517
|
-
NSValue *oldSize = change[NSKeyValueChangeOldKey];
|
|
518
|
-
NSValue *newSize = change[NSKeyValueChangeNewKey];
|
|
519
|
-
if (![oldSize isEqual:newSize]) {
|
|
520
|
-
[self applyInsetsToScrollView:_observedScrollView pageIndex:_currentIndex restore:NO];
|
|
521
|
-
}
|
|
522
|
-
return;
|
|
523
|
-
}
|
|
524
|
-
UIScrollView *scrollView = (UIScrollView *)object;
|
|
525
|
-
_pageOffsets[self.currentPageKey] = @(scrollView.contentOffset.y + scrollView.contentInset.top);
|
|
526
|
-
[self updateHeaderForScrollView:scrollView];
|
|
527
|
-
return;
|
|
528
|
-
}
|
|
529
|
-
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
- (void)updateHeaderForScrollView:(UIScrollView *)scrollView
|
|
533
|
-
{
|
|
534
|
-
CGFloat logicalOffset = scrollView.contentOffset.y + scrollView.contentInset.top;
|
|
535
|
-
_headerOffset = MIN(MAX(logicalOffset, 0), _headerHeight);
|
|
536
|
-
[self applyHeaderOffset];
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
- (void)applyHeaderOffset
|
|
540
|
-
{
|
|
541
|
-
CGAffineTransform transform = CGAffineTransformMakeTranslation(0, -_headerOffset);
|
|
542
|
-
_headerView.transform = transform;
|
|
543
|
-
_stickyHeaderView.transform = transform;
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
#pragma mark - Pager navigation
|
|
547
|
-
|
|
548
|
-
- (BOOL)isLtrLayout
|
|
549
|
-
{
|
|
550
|
-
return [_layoutDirection isEqualToString:@"ltr"];
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
- (void)goTo:(NSInteger)index animated:(BOOL)animated
|
|
554
|
-
{
|
|
555
|
-
if (_isBeingRecycled || index < 0 || index >= _pageControllers.count) return;
|
|
556
|
-
if (_transitioning && animated) return;
|
|
557
|
-
|
|
558
|
-
[self detachScrollObserver];
|
|
559
|
-
_destinationIndex = index;
|
|
560
|
-
BOOL forward = (index > _currentIndex && self.isLtrLayout) ||
|
|
561
|
-
(index < _currentIndex && !self.isLtrLayout);
|
|
562
|
-
UIPageViewControllerNavigationDirection direction = forward
|
|
563
|
-
? UIPageViewControllerNavigationDirectionForward
|
|
564
|
-
: UIPageViewControllerNavigationDirectionReverse;
|
|
565
|
-
_transitioning = YES;
|
|
566
|
-
NSUInteger capturedGeneration = _generation;
|
|
567
|
-
__weak __typeof__(self) weakSelf = self;
|
|
568
|
-
[_pageViewController setViewControllers:@[_pageControllers[index]]
|
|
569
|
-
direction:direction
|
|
570
|
-
animated:animated && index != _currentIndex
|
|
571
|
-
completion:^(BOOL finished) {
|
|
572
|
-
__strong __typeof__(weakSelf) self = weakSelf;
|
|
573
|
-
if (self == nil || self->_generation != capturedGeneration || self->_isBeingRecycled) return;
|
|
574
|
-
self->_transitioning = NO;
|
|
575
|
-
self->_currentIndex = index;
|
|
576
|
-
[self attachScrollObserverForCurrentPage];
|
|
577
|
-
[self emitPageSelected:index];
|
|
578
|
-
[self emitDiagnostics:@"page-selected"];
|
|
579
|
-
}];
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
- (UIViewController *)adjacentController:(UIViewController *)controller delta:(NSInteger)delta
|
|
583
|
-
{
|
|
584
|
-
NSInteger index = [_pageControllers indexOfObjectIdenticalTo:controller];
|
|
585
|
-
if (index == NSNotFound) return nil;
|
|
586
|
-
NSInteger target = index + delta;
|
|
587
|
-
if (target < 0 || target >= _pageControllers.count) return nil;
|
|
588
|
-
UIScrollView *scrollView = [self findVerticalScrollViewInView:_pageControllers[target].view];
|
|
589
|
-
if (scrollView != nil) {
|
|
590
|
-
[self applyInsetsToScrollView:scrollView pageIndex:target restore:YES];
|
|
591
|
-
}
|
|
592
|
-
return _pageControllers[target];
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
|
|
596
|
-
viewControllerAfterViewController:(UIViewController *)viewController
|
|
597
|
-
{
|
|
598
|
-
return [self adjacentController:viewController delta:self.isLtrLayout ? 1 : -1];
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
|
|
602
|
-
viewControllerBeforeViewController:(UIViewController *)viewController
|
|
603
|
-
{
|
|
604
|
-
return [self adjacentController:viewController delta:self.isLtrLayout ? -1 : 1];
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
- (void)pageViewController:(UIPageViewController *)pageViewController
|
|
608
|
-
didFinishAnimating:(BOOL)finished
|
|
609
|
-
previousViewControllers:(NSArray<UIViewController *> *)previousViewControllers
|
|
610
|
-
transitionCompleted:(BOOL)completed
|
|
611
|
-
{
|
|
612
|
-
_transitioning = NO;
|
|
613
|
-
if (!completed) return;
|
|
614
|
-
UIViewController *controller = _pageViewController.viewControllers.firstObject;
|
|
615
|
-
NSInteger index = [_pageControllers indexOfObjectIdenticalTo:controller];
|
|
616
|
-
if (index == NSNotFound) return;
|
|
617
|
-
[self detachScrollObserver];
|
|
618
|
-
_currentIndex = index;
|
|
619
|
-
[self attachScrollObserverForCurrentPage];
|
|
620
|
-
[self emitPageSelected:index];
|
|
621
|
-
[self emitDiagnostics:@"page-selected"];
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
#pragma mark - Pager UIScrollViewDelegate
|
|
625
|
-
|
|
626
|
-
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
|
|
627
|
-
{
|
|
628
|
-
const auto emitter = [self eventEmitter];
|
|
629
|
-
if (emitter) {
|
|
630
|
-
emitter->onPageScrollStateChanged({
|
|
631
|
-
.pageScrollState = RNCCollapsiblePagerViewEventEmitter::OnPageScrollStateChangedPageScrollState::Dragging
|
|
632
|
-
});
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
|
|
637
|
-
withVelocity:(CGPoint)velocity
|
|
638
|
-
targetContentOffset:(inout CGPoint *)targetContentOffset
|
|
639
|
-
{
|
|
640
|
-
const auto emitter = [self eventEmitter];
|
|
641
|
-
if (emitter) {
|
|
642
|
-
emitter->onPageScrollStateChanged({
|
|
643
|
-
.pageScrollState = RNCCollapsiblePagerViewEventEmitter::OnPageScrollStateChangedPageScrollState::Settling
|
|
644
|
-
});
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
|
|
649
|
-
{
|
|
650
|
-
const auto emitter = [self eventEmitter];
|
|
651
|
-
if (emitter) {
|
|
652
|
-
emitter->onPageScrollStateChanged({
|
|
653
|
-
.pageScrollState = RNCCollapsiblePagerViewEventEmitter::OnPageScrollStateChangedPageScrollState::Idle
|
|
654
|
-
});
|
|
655
|
-
}
|
|
656
|
-
[self emitDiagnostics:@"pager-idle"];
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
|
|
660
|
-
{
|
|
661
|
-
CGFloat width = scrollView.bounds.size.width;
|
|
662
|
-
if (width <= 0) return;
|
|
663
|
-
CGFloat rawOffset = (scrollView.contentOffset.x - width) / width;
|
|
664
|
-
BOOL backwards = self.isLtrLayout ? rawOffset < 0 : rawOffset > 0;
|
|
665
|
-
NSInteger position = backwards ? _currentIndex - 1 : _currentIndex;
|
|
666
|
-
CGFloat offset = backwards ? 1 - fabs(rawOffset) : fabs(rawOffset);
|
|
667
|
-
position = MAX(0, MIN(position, (NSInteger)_pageControllers.count - 1));
|
|
668
|
-
const auto emitter = [self eventEmitter];
|
|
669
|
-
if (emitter) {
|
|
670
|
-
emitter->onPageScroll({.position = (double)position, .offset = (double)offset});
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
#pragma mark - Events and commands
|
|
675
|
-
|
|
676
|
-
- (std::shared_ptr<const RNCCollapsiblePagerViewEventEmitter>)eventEmitter
|
|
677
|
-
{
|
|
678
|
-
if (!_eventEmitter) return nullptr;
|
|
679
|
-
return std::static_pointer_cast<const RNCCollapsiblePagerViewEventEmitter>(_eventEmitter);
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
- (void)emitPageSelected:(NSInteger)position
|
|
683
|
-
{
|
|
684
|
-
const auto emitter = [self eventEmitter];
|
|
685
|
-
if (emitter) emitter->onPageSelected({.position = (double)position});
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
- (void)emitDiagnostics:(NSString *)reason
|
|
689
|
-
{
|
|
690
|
-
const auto emitter = [self eventEmitter];
|
|
691
|
-
if (!emitter) return;
|
|
692
|
-
NSInteger attached = 0;
|
|
693
|
-
for (UIViewController *controller in _pageControllers) {
|
|
694
|
-
if (controller.view.superview != nil) attached++;
|
|
695
|
-
}
|
|
696
|
-
emitter->onCollapsibleStateChanged({
|
|
697
|
-
.position = (int)_currentIndex,
|
|
698
|
-
.headerOffset = (double)_headerOffset,
|
|
699
|
-
.nativePageCount = (int)_pageControllers.count,
|
|
700
|
-
.attachedPageCount = (int)attached,
|
|
701
|
-
.observedScrollableCount = (int)_originalInsets.count,
|
|
702
|
-
.retainedPages = std::string(_retainedPages.UTF8String ?: "[]"),
|
|
703
|
-
.reason = std::string(reason.UTF8String ?: "unknown")
|
|
704
|
-
});
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
- (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args
|
|
708
|
-
{
|
|
709
|
-
RCTRNCCollapsiblePagerViewHandleCommand(self, commandName, args);
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
- (void)setPage:(NSInteger)index
|
|
713
|
-
{
|
|
714
|
-
[self goTo:index animated:YES];
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
- (void)setPageWithoutAnimation:(NSInteger)index
|
|
718
|
-
{
|
|
719
|
-
[self goTo:index animated:NO];
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
- (void)setScrollEnabledImperatively:(BOOL)scrollEnabled
|
|
723
|
-
{
|
|
724
|
-
_scrollEnabled = scrollEnabled;
|
|
725
|
-
_pagerScrollView.scrollEnabled = scrollEnabled;
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
|
729
|
-
{
|
|
730
|
-
return concreteComponentDescriptorProvider<RNCCollapsiblePagerViewComponentDescriptor>();
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
@end
|
|
734
|
-
|
|
735
|
-
Class<RCTComponentViewProtocol> RNCCollapsiblePagerViewCls(void)
|
|
736
|
-
{
|
|
737
|
-
return RNCCollapsiblePagerViewComponentView.class;
|
|
738
|
-
}
|