1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | #import "RCTScrollViewManager.h"
|
9 |
|
10 | #import "RCTBridge.h"
|
11 | #import "RCTScrollView.h"
|
12 | #import "RCTShadowView.h"
|
13 | #import "RCTUIManager.h"
|
14 |
|
15 | @interface RCTScrollView (Private)
|
16 |
|
17 | - (NSArray<NSDictionary *> *)calculateChildFramesData;
|
18 |
|
19 | @end
|
20 |
|
21 | @implementation RCTConvert (UIScrollView)
|
22 |
|
23 | RCT_ENUM_CONVERTER(UIScrollViewKeyboardDismissMode, (@{
|
24 | @"none": @(UIScrollViewKeyboardDismissModeNone),
|
25 | @"on-drag": @(UIScrollViewKeyboardDismissModeOnDrag),
|
26 | @"interactive": @(UIScrollViewKeyboardDismissModeInteractive),
|
27 | // Backwards compatibility
|
28 | @"onDrag": @(UIScrollViewKeyboardDismissModeOnDrag),
|
29 | }), UIScrollViewKeyboardDismissModeNone, integerValue)
|
30 |
|
31 | RCT_ENUM_CONVERTER(UIScrollViewIndicatorStyle, (@{
|
32 | @"default": @(UIScrollViewIndicatorStyleDefault),
|
33 | @"black": @(UIScrollViewIndicatorStyleBlack),
|
34 | @"white": @(UIScrollViewIndicatorStyleWhite),
|
35 | }), UIScrollViewIndicatorStyleDefault, integerValue)
|
36 |
|
37 | #pragma clang diagnostic push
|
38 | #pragma clang diagnostic ignored "-Wunguarded-availability-new"
|
39 | #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000
|
40 | RCT_ENUM_CONVERTER(UIScrollViewContentInsetAdjustmentBehavior, (@{
|
41 | @"automatic": @(UIScrollViewContentInsetAdjustmentAutomatic),
|
42 | @"scrollableAxes": @(UIScrollViewContentInsetAdjustmentScrollableAxes),
|
43 | @"never": @(UIScrollViewContentInsetAdjustmentNever),
|
44 | @"always": @(UIScrollViewContentInsetAdjustmentAlways),
|
45 | }), UIScrollViewContentInsetAdjustmentNever, integerValue)
|
46 | #endif
|
47 | #pragma clang diagnostic pop
|
48 |
|
49 | @end
|
50 |
|
51 | @implementation RCTScrollViewManager
|
52 |
|
53 | RCT_EXPORT_MODULE()
|
54 |
|
55 | - (UIView *)view
|
56 | {
|
57 | return [[RCTScrollView alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
|
58 | }
|
59 |
|
60 | RCT_EXPORT_VIEW_PROPERTY(alwaysBounceHorizontal, BOOL)
|
61 | RCT_EXPORT_VIEW_PROPERTY(alwaysBounceVertical, BOOL)
|
62 | RCT_EXPORT_VIEW_PROPERTY(bounces, BOOL)
|
63 | RCT_EXPORT_VIEW_PROPERTY(bouncesZoom, BOOL)
|
64 | RCT_EXPORT_VIEW_PROPERTY(canCancelContentTouches, BOOL)
|
65 | RCT_EXPORT_VIEW_PROPERTY(centerContent, BOOL)
|
66 | RCT_EXPORT_VIEW_PROPERTY(maintainVisibleContentPosition, NSDictionary)
|
67 | RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustContentInsets, BOOL)
|
68 | RCT_EXPORT_VIEW_PROPERTY(decelerationRate, CGFloat)
|
69 | RCT_EXPORT_VIEW_PROPERTY(directionalLockEnabled, BOOL)
|
70 | RCT_EXPORT_VIEW_PROPERTY(indicatorStyle, UIScrollViewIndicatorStyle)
|
71 | RCT_EXPORT_VIEW_PROPERTY(keyboardDismissMode, UIScrollViewKeyboardDismissMode)
|
72 | RCT_EXPORT_VIEW_PROPERTY(maximumZoomScale, CGFloat)
|
73 | RCT_EXPORT_VIEW_PROPERTY(minimumZoomScale, CGFloat)
|
74 | RCT_EXPORT_VIEW_PROPERTY(scrollEnabled, BOOL)
|
75 | #if !TARGET_OS_TV
|
76 | RCT_EXPORT_VIEW_PROPERTY(pagingEnabled, BOOL)
|
77 | RCT_REMAP_VIEW_PROPERTY(pinchGestureEnabled, scrollView.pinchGestureEnabled, BOOL)
|
78 | RCT_EXPORT_VIEW_PROPERTY(scrollsToTop, BOOL)
|
79 | #endif
|
80 | RCT_EXPORT_VIEW_PROPERTY(showsHorizontalScrollIndicator, BOOL)
|
81 | RCT_EXPORT_VIEW_PROPERTY(showsVerticalScrollIndicator, BOOL)
|
82 | RCT_EXPORT_VIEW_PROPERTY(scrollEventThrottle, NSTimeInterval)
|
83 | RCT_EXPORT_VIEW_PROPERTY(zoomScale, CGFloat)
|
84 | RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets)
|
85 | RCT_EXPORT_VIEW_PROPERTY(scrollIndicatorInsets, UIEdgeInsets)
|
86 | RCT_EXPORT_VIEW_PROPERTY(scrollToOverflowEnabled, BOOL)
|
87 | RCT_EXPORT_VIEW_PROPERTY(snapToInterval, int)
|
88 | RCT_EXPORT_VIEW_PROPERTY(disableIntervalMomentum, BOOL)
|
89 | RCT_EXPORT_VIEW_PROPERTY(snapToOffsets, NSArray<NSNumber *>)
|
90 | RCT_EXPORT_VIEW_PROPERTY(snapToStart, BOOL)
|
91 | RCT_EXPORT_VIEW_PROPERTY(snapToEnd, BOOL)
|
92 | RCT_EXPORT_VIEW_PROPERTY(snapToAlignment, NSString)
|
93 | RCT_REMAP_VIEW_PROPERTY(contentOffset, scrollView.contentOffset, CGPoint)
|
94 | RCT_EXPORT_VIEW_PROPERTY(onScrollBeginDrag, RCTDirectEventBlock)
|
95 | RCT_EXPORT_VIEW_PROPERTY(onScroll, RCTDirectEventBlock)
|
96 | RCT_EXPORT_VIEW_PROPERTY(onScrollToTop, RCTDirectEventBlock)
|
97 | RCT_EXPORT_VIEW_PROPERTY(onScrollEndDrag, RCTDirectEventBlock)
|
98 | RCT_EXPORT_VIEW_PROPERTY(onMomentumScrollBegin, RCTDirectEventBlock)
|
99 | RCT_EXPORT_VIEW_PROPERTY(onMomentumScrollEnd, RCTDirectEventBlock)
|
100 | RCT_EXPORT_VIEW_PROPERTY(DEPRECATED_sendUpdatedChildFrames, BOOL)
|
101 | RCT_EXPORT_VIEW_PROPERTY(inverted, BOOL)
|
102 | #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000
|
103 | RCT_EXPORT_VIEW_PROPERTY(contentInsetAdjustmentBehavior, UIScrollViewContentInsetAdjustmentBehavior)
|
104 | #endif
|
105 |
|
106 | // overflow is used both in css-layout as well as by react-native. In css-layout
|
107 | // we always want to treat overflow as scroll but depending on what the overflow
|
108 | // is set to from js we want to clip drawing or not. This piece of code ensures
|
109 | // that css-layout is always treating the contents of a scroll container as
|
110 | // overflow: 'scroll'.
|
111 | RCT_CUSTOM_SHADOW_PROPERTY(overflow, YGOverflow, RCTShadowView) {
|
112 | #pragma unused (json)
|
113 | view.overflow = YGOverflowScroll;
|
114 | }
|
115 |
|
116 | RCT_EXPORT_METHOD(getContentSize:(nonnull NSNumber *)reactTag
|
117 | callback:(RCTResponseSenderBlock)callback)
|
118 | {
|
119 | [self.bridge.uiManager addUIBlock:
|
120 | ^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTScrollView *> *viewRegistry) {
|
121 |
|
122 | RCTScrollView *view = viewRegistry[reactTag];
|
123 | if (!view || ![view isKindOfClass:[RCTScrollView class]]) {
|
124 | RCTLogError(@"Cannot find RCTScrollView with tag #%@", reactTag);
|
125 | return;
|
126 | }
|
127 |
|
128 | CGSize size = view.scrollView.contentSize;
|
129 | callback(@[@{
|
130 | @"width" : @(size.width),
|
131 | @"height" : @(size.height)
|
132 | }]);
|
133 | }];
|
134 | }
|
135 |
|
136 | RCT_EXPORT_METHOD(calculateChildFrames:(nonnull NSNumber *)reactTag
|
137 | callback:(RCTResponseSenderBlock)callback)
|
138 | {
|
139 | [self.bridge.uiManager addUIBlock:
|
140 | ^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTScrollView *> *viewRegistry) {
|
141 |
|
142 | RCTScrollView *view = viewRegistry[reactTag];
|
143 | if (!view || ![view isKindOfClass:[RCTScrollView class]]) {
|
144 | RCTLogError(@"Cannot find RCTScrollView with tag #%@", reactTag);
|
145 | return;
|
146 | }
|
147 |
|
148 | NSArray<NSDictionary *> *childFrames = [view calculateChildFramesData];
|
149 | if (childFrames) {
|
150 | callback(@[childFrames]);
|
151 | }
|
152 | }];
|
153 | }
|
154 |
|
155 | RCT_EXPORT_METHOD(scrollTo:(nonnull NSNumber *)reactTag
|
156 | offsetX:(CGFloat)x
|
157 | offsetY:(CGFloat)y
|
158 | animated:(BOOL)animated)
|
159 | {
|
160 | [self.bridge.uiManager addUIBlock:
|
161 | ^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry){
|
162 | UIView *view = viewRegistry[reactTag];
|
163 | if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) {
|
164 | [(id<RCTScrollableProtocol>)view scrollToOffset:(CGPoint){x, y} animated:animated];
|
165 | } else {
|
166 | RCTLogError(@"tried to scrollTo: on non-RCTScrollableProtocol view %@ "
|
167 | "with tag #%@", view, reactTag);
|
168 | }
|
169 | }];
|
170 | }
|
171 |
|
172 | RCT_EXPORT_METHOD(scrollToEnd:(nonnull NSNumber *)reactTag
|
173 | animated:(BOOL)animated)
|
174 | {
|
175 | [self.bridge.uiManager addUIBlock:
|
176 | ^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry){
|
177 | UIView *view = viewRegistry[reactTag];
|
178 | if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) {
|
179 | [(id<RCTScrollableProtocol>)view scrollToEnd:animated];
|
180 | } else {
|
181 | RCTLogError(@"tried to scrollTo: on non-RCTScrollableProtocol view %@ "
|
182 | "with tag #%@", view, reactTag);
|
183 | }
|
184 | }];
|
185 | }
|
186 |
|
187 | RCT_EXPORT_METHOD(zoomToRect:(nonnull NSNumber *)reactTag
|
188 | withRect:(CGRect)rect
|
189 | animated:(BOOL)animated)
|
190 | {
|
191 | [self.bridge.uiManager addUIBlock:
|
192 | ^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry){
|
193 | UIView *view = viewRegistry[reactTag];
|
194 | if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) {
|
195 | [(id<RCTScrollableProtocol>)view zoomToRect:rect animated:animated];
|
196 | } else {
|
197 | RCTLogError(@"tried to zoomToRect: on non-RCTScrollableProtocol view %@ "
|
198 | "with tag #%@", view, reactTag);
|
199 | }
|
200 | }];
|
201 | }
|
202 |
|
203 | RCT_EXPORT_METHOD(flashScrollIndicators:(nonnull NSNumber *)reactTag)
|
204 | {
|
205 | [self.bridge.uiManager addUIBlock:
|
206 | ^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTScrollView *> *viewRegistry){
|
207 |
|
208 | RCTScrollView *view = viewRegistry[reactTag];
|
209 | if (!view || ![view isKindOfClass:[RCTScrollView class]]) {
|
210 | RCTLogError(@"Cannot find RCTScrollView with tag #%@", reactTag);
|
211 | return;
|
212 | }
|
213 |
|
214 | [view.scrollView flashScrollIndicators];
|
215 | }];
|
216 | }
|
217 |
|
218 | @end
|