Skip to content

Commit bdf7de4

Browse files
authored
feat(iOS): rewrite old arch to use UIScrollView (#681)
* feat: rewrite old arch to use UIScrollView * feat: update example styles * fix: sending event on scrollViewDidEndDecelerating * feat: properly calculate width using orientation * fix: change way of disabing scroll * feat: rename to RNCPagerView * fix: removing last page * fix: remove unused properties, set animated
1 parent 0af8914 commit bdf7de4

File tree

6 files changed

+205
-489
lines changed

6 files changed

+205
-489
lines changed

example/ios/Podfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ PODS:
302302
- React-jsinspector (0.70.5)
303303
- React-logger (0.70.5):
304304
- glog
305-
- react-native-pager-view (6.1.1):
305+
- react-native-pager-view (6.1.2):
306306
- React-Core
307307
- react-native-safe-area-context (3.4.1):
308308
- React-Core
@@ -617,7 +617,7 @@ SPEC CHECKSUMS:
617617
React-jsiexecutor: 31564fa6912459921568e8b0e49024285a4d584b
618618
React-jsinspector: badd81696361249893a80477983e697aab3c1a34
619619
React-logger: fdda34dd285bdb0232e059b19d9606fa0ec3bb9c
620-
react-native-pager-view: 3c66c4e2f3ab423643d07b2c7041f8ac48395f72
620+
react-native-pager-view: 54bed894cecebe28cede54c01038d9d1e122de43
621621
react-native-safe-area-context: 9e40fb181dac02619414ba1294d6c2a807056ab9
622622
React-perflogger: e68d3795cf5d247a0379735cbac7309adf2fb931
623623
React-RCTActionSheet: 05452c3b281edb27850253db13ecd4c5a65bc247

ios/ReactNativePageView.h renamed to ios/RNCPagerView.h

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,17 @@
55

66
NS_ASSUME_NONNULL_BEGIN
77

8-
@interface ReactNativePageView: UIView
8+
@interface RNCPagerView: UIView
99

10-
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher;
10+
- (instancetype)initWithEventDispatcher:(id<RCTEventDispatcherProtocol> )eventDispatcher;
1111

1212
@property(nonatomic) NSInteger initialPage;
13-
@property(nonatomic) NSInteger lastReportedIndex;
14-
@property(nonatomic) NSInteger destinationIndex;
15-
@property(nonatomic) NSInteger currentIndex;
16-
@property(nonatomic) NSInteger pageMargin;
17-
@property(nonatomic, readonly) BOOL scrollEnabled;
13+
@property(nonatomic) NSString* orientation;
1814
@property(nonatomic, readonly) UIScrollViewKeyboardDismissMode dismissKeyboard;
19-
@property(nonatomic) UIPageViewControllerNavigationOrientation orientation;
2015
@property(nonatomic, copy) RCTDirectEventBlock onPageSelected;
2116
@property(nonatomic, copy) RCTDirectEventBlock onPageScroll;
2217
@property(nonatomic, copy) RCTDirectEventBlock onPageScrollStateChanged;
2318
@property(nonatomic) BOOL overdrag;
24-
@property(nonatomic) NSString* layoutDirection;
2519
@property(nonatomic, assign) BOOL animating;
2620

2721
- (void)goTo:(NSInteger)index animated:(BOOL)animated;

ios/RNCPagerView.m

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
2+
#import "RNCPagerView.h"
3+
#import "React/RCTLog.h"
4+
#import <React/RCTViewManager.h>
5+
6+
#import "UIViewController+CreateExtension.h"
7+
#import "RCTOnPageScrollEvent.h"
8+
#import "RCTOnPageScrollStateChanged.h"
9+
#import "React/RCTUIManagerObserverCoordinator.h"
10+
#import "RCTOnPageSelected.h"
11+
#import <math.h>
12+
13+
@interface RNCPagerView () <UIScrollViewDelegate>
14+
15+
@property(nonatomic, strong) id<RCTEventDispatcherProtocol> eventDispatcher;
16+
17+
@property(nonatomic, strong) UIScrollView *scrollView;
18+
@property(nonatomic, strong) UIView *containerView;
19+
20+
- (void)goTo:(NSInteger)index animated:(BOOL)animated;
21+
- (void)shouldScroll:(BOOL)scrollEnabled;
22+
- (void)shouldDismissKeyboard:(NSString *)dismissKeyboard;
23+
24+
@end
25+
26+
@implementation RNCPagerView {
27+
uint16_t _coalescingKey;
28+
}
29+
30+
- (instancetype)initWithEventDispatcher:(id<RCTEventDispatcherProtocol>)eventDispatcher {
31+
if (self = [super init]) {
32+
_initialPage = 0;
33+
_dismissKeyboard = UIScrollViewKeyboardDismissModeNone;
34+
_coalescingKey = 0;
35+
_eventDispatcher = eventDispatcher;
36+
_orientation = @"horizontal";
37+
[self embed];
38+
}
39+
return self;
40+
}
41+
42+
- (void)layoutSubviews {
43+
[super layoutSubviews];
44+
[self calculateContentSize];
45+
}
46+
47+
- (void)didUpdateReactSubviews {
48+
[self calculateContentSize];
49+
}
50+
51+
-(void) insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex {
52+
[super insertReactSubview:subview atIndex:atIndex];
53+
[_containerView insertSubview:subview atIndex:atIndex];
54+
}
55+
56+
- (void)removeReactSubview:(UIView *)subview {
57+
[super removeReactSubview:subview];
58+
[subview removeFromSuperview];
59+
60+
if ([self getCurrentPage] >= self.reactSubviews.count - 1) {
61+
[self goTo:self.reactSubviews.count - 1 animated:false];
62+
}
63+
}
64+
65+
- (void)embed {
66+
_scrollView = [[UIScrollView alloc] initWithFrame:self.bounds];
67+
_scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
68+
_scrollView.delaysContentTouches = NO;
69+
_scrollView.delegate = self;
70+
_scrollView.pagingEnabled = YES;
71+
_scrollView.showsHorizontalScrollIndicator = NO;
72+
_scrollView.showsVerticalScrollIndicator = NO;
73+
[self addSubview:_scrollView];
74+
75+
_containerView = [[UIView alloc] initWithFrame:self.bounds];
76+
[_scrollView addSubview:_containerView];
77+
}
78+
79+
- (void)didSetProps:(NSArray<NSString *> *)changedProps {
80+
if ([changedProps containsObject:@"overdrag"]) {
81+
[_scrollView setBounces:_overdrag];
82+
}
83+
}
84+
85+
- (void)shouldScroll:(BOOL)scrollEnabled {
86+
if (self.scrollView) {
87+
self.scrollView.scrollEnabled = scrollEnabled;
88+
}
89+
}
90+
91+
- (void)shouldDismissKeyboard:(NSString *)dismissKeyboard {
92+
_dismissKeyboard = [dismissKeyboard isEqual: @"on-drag"] ?
93+
UIScrollViewKeyboardDismissModeOnDrag : UIScrollViewKeyboardDismissModeNone;
94+
self.scrollView.keyboardDismissMode = _dismissKeyboard;
95+
}
96+
97+
#pragma mark - Internal methods
98+
99+
- (void) calculateContentSize {
100+
UIView *initialView = self.containerView.subviews.firstObject;
101+
if (!initialView) {
102+
return;
103+
}
104+
105+
CGFloat totalSubviewsWidth = initialView.frame.size.width * self.containerView.subviews.count;
106+
CGFloat totalSubviewsHeight = initialView.frame.size.height * self.containerView.subviews.count;
107+
108+
109+
if ([self isHorizontal]) {
110+
_scrollView.contentSize = CGSizeMake(totalSubviewsWidth, 0);
111+
_containerView.frame = CGRectMake(0, 0, totalSubviewsWidth, initialView.bounds.size.height);
112+
} else {
113+
_scrollView.contentSize = CGSizeMake(0, totalSubviewsHeight);
114+
_containerView.frame = CGRectMake(0, 0, initialView.bounds.size.width, totalSubviewsHeight);
115+
}
116+
117+
_scrollView.frame = self.bounds;
118+
[self.scrollView layoutIfNeeded];
119+
120+
if (self.initialPage != 0) {
121+
[self goTo:self.initialPage animated:false];
122+
}
123+
}
124+
125+
- (void)disableSwipe {
126+
[_scrollView setScrollEnabled:false];
127+
}
128+
129+
- (void)enableSwipe {
130+
[_scrollView setScrollEnabled:true];
131+
}
132+
133+
- (void)goTo:(NSInteger)index animated:(BOOL)animated {
134+
CGPoint targetOffset = [self isHorizontal] ? CGPointMake(_scrollView.frame.size.width * index, 0) : CGPointMake(0, _scrollView.frame.size.height * index);
135+
136+
if (animated) {
137+
self.animating = true;
138+
}
139+
140+
[_scrollView setContentOffset:targetOffset animated:animated];
141+
142+
if (!animated) {
143+
int position = [self getCurrentPage];
144+
[self.eventDispatcher sendEvent:[[RCTOnPageSelected alloc] initWithReactTag:self.reactTag position:[NSNumber numberWithInt:position] coalescingKey:_coalescingKey++]];
145+
}
146+
}
147+
148+
- (BOOL)isHorizontal {
149+
return [_orientation isEqualToString:@"horizontal"];
150+
}
151+
152+
-(int)getCurrentPage {
153+
return [self isHorizontal] ? _scrollView.contentOffset.x / _scrollView.frame.size.width : _scrollView.contentOffset.y / _scrollView.frame.size.height;
154+
}
155+
156+
#pragma mark - UIScrollViewDelegate
157+
158+
159+
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
160+
[self.eventDispatcher sendEvent:[[RCTOnPageScrollStateChanged alloc] initWithReactTag:self.reactTag state:@"dragging" coalescingKey:_coalescingKey++]];
161+
}
162+
163+
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
164+
[self.eventDispatcher sendEvent:[[RCTOnPageScrollStateChanged alloc] initWithReactTag:self.reactTag state:@"settling" coalescingKey:_coalescingKey++]];
165+
}
166+
167+
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
168+
int position = [self getCurrentPage];
169+
[self.eventDispatcher sendEvent:[[RCTOnPageScrollStateChanged alloc] initWithReactTag:self.reactTag state:@"idle" coalescingKey:_coalescingKey++]];
170+
171+
[self.eventDispatcher sendEvent:[[RCTOnPageSelected alloc] initWithReactTag:self.reactTag position:[NSNumber numberWithInt:position] coalescingKey:_coalescingKey++]];
172+
}
173+
174+
-(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
175+
int position = [self getCurrentPage];
176+
self.animating = false;
177+
[self.eventDispatcher sendEvent:[[RCTOnPageSelected alloc] initWithReactTag:self.reactTag position:[NSNumber numberWithInt:position] coalescingKey:_coalescingKey++]];
178+
}
179+
180+
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
181+
int position = [self getCurrentPage];
182+
183+
double offset = [self isHorizontal] ? (scrollView.contentOffset.x - (scrollView.frame.size.width * position))/scrollView.frame.size.width : (scrollView.contentOffset.y - (scrollView.frame.size.height * position))/scrollView.frame.size.height;
184+
185+
[self.eventDispatcher sendEvent:[[RCTOnPageScrollEvent alloc] initWithReactTag:self.reactTag position:@(position) offset:@(offset)]];
186+
}
187+
@end
188+

ios/ReactViewPagerManager.h renamed to ios/RNCPagerViewManager.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
#import <React/RCTViewManager.h>
44
#import <React/RCTUIManager.h>
55
#import <React/RCTLog.h>
6-
#import "ReactNativePageView.h"
6+
#import "RNCPagerView.h"
77
NS_ASSUME_NONNULL_BEGIN
88

9-
@interface ReactViewPagerManager : RCTViewManager
9+
@interface RNCPagerViewManager : RCTViewManager
1010

1111
@end
1212

ios/ReactViewPagerManager.m renamed to ios/RNCPagerViewManager.m

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
11

2-
#import "ReactViewPagerManager.h"
2+
#import "RNCPagerViewManager.h"
33

4-
@implementation ReactViewPagerManager
4+
@implementation RNCPagerViewManager
55

66
#pragma mark - RTC
77

88
RCT_EXPORT_MODULE(RNCViewPager)
99

1010
RCT_EXPORT_VIEW_PROPERTY(initialPage, NSInteger)
11-
RCT_EXPORT_VIEW_PROPERTY(pageMargin, NSInteger)
11+
RCT_EXPORT_VIEW_PROPERTY(orientation, NSString)
1212

13-
RCT_EXPORT_VIEW_PROPERTY(orientation, UIPageViewControllerNavigationOrientation)
1413
RCT_EXPORT_VIEW_PROPERTY(onPageSelected, RCTDirectEventBlock)
1514
RCT_EXPORT_VIEW_PROPERTY(onPageScroll, RCTDirectEventBlock)
1615
RCT_EXPORT_VIEW_PROPERTY(onPageScrollStateChanged, RCTDirectEventBlock)
1716
RCT_EXPORT_VIEW_PROPERTY(overdrag, BOOL)
18-
RCT_EXPORT_VIEW_PROPERTY(layoutDirection, NSString)
1917

2018

2119
- (void) goToPage
@@ -25,8 +23,8 @@ - (void) goToPage
2523
[self.bridge.uiManager addUIBlock:^(
2624
RCTUIManager *uiManager,
2725
NSDictionary<NSNumber *, UIView *> *viewRegistry) {
28-
ReactNativePageView *view = (ReactNativePageView *)viewRegistry[reactTag];
29-
if (!view || ![view isKindOfClass:[ReactNativePageView class]]) {
26+
RNCPagerView *view = (RNCPagerView *)viewRegistry[reactTag];
27+
if (!view || ![view isKindOfClass:[RNCPagerView class]]) {
3028
RCTLogError(@"Cannot find ReactNativePageView with tag #%@", reactTag);
3129
return;
3230
}
@@ -42,8 +40,8 @@ - (void) changeScrollEnabled
4240
[self.bridge.uiManager addUIBlock:^(
4341
RCTUIManager *uiManager,
4442
NSDictionary<NSNumber *, UIView *> *viewRegistry) {
45-
ReactNativePageView *view = (ReactNativePageView *)viewRegistry[reactTag];
46-
if (!view || ![view isKindOfClass:[ReactNativePageView class]]) {
43+
RNCPagerView *view = (RNCPagerView *)viewRegistry[reactTag];
44+
if (!view || ![view isKindOfClass:[RNCPagerView class]]) {
4745
RCTLogError(@"Cannot find ReactNativePageView with tag #%@", reactTag);
4846
return;
4947
}
@@ -70,17 +68,17 @@ - (void) changeScrollEnabled
7068
[self changeScrollEnabled:reactTag enabled:isEnabled];
7169
}
7270

73-
RCT_CUSTOM_VIEW_PROPERTY(scrollEnabled, BOOL, ReactNativePageView) {
71+
RCT_CUSTOM_VIEW_PROPERTY(scrollEnabled, BOOL, RNCPagerView) {
7472
[view shouldScroll:[RCTConvert BOOL:json]];
7573
}
7674

77-
RCT_CUSTOM_VIEW_PROPERTY(keyboardDismissMode, NSString, ReactNativePageView) {
75+
RCT_CUSTOM_VIEW_PROPERTY(keyboardDismissMode, NSString, RNCPagerView) {
7876
[view shouldDismissKeyboard:[RCTConvert NSString:json]];
7977
}
8078

8179

8280
- (UIView *)view {
83-
return [[ReactNativePageView alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
81+
return [[RNCPagerView alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
8482
}
8583

8684
@end

0 commit comments

Comments
 (0)