@sdcx/bottom-sheet 0.17.0 → 1.0.0
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 +57 -51
- package/RNBottomSheet.podspec +5 -4
- package/android/build.gradle +42 -19
- package/android/src/main/java/com/reactnative/bottomsheet/BottomSheet.java +59 -58
- package/android/src/main/java/com/reactnative/bottomsheet/BottomSheetManager.java +12 -6
- package/android/src/main/java/com/reactnative/bottomsheet/{BottomSheetState.java → BottomSheetStatus.java} +1 -1
- package/android/src/main/java/com/reactnative/bottomsheet/{OffsetChangedEvent.java → OnSlideEvent.java} +5 -3
- package/android/src/main/java/com/reactnative/bottomsheet/{StateChangedEvent.java → OnStateChangedEvent.java} +5 -3
- package/dist/BottomSheetNativeComponent.d.ts +20 -0
- package/dist/BottomSheetNativeComponent.js +2 -0
- package/dist/index.d.ts +8 -15
- package/dist/index.js +9 -9
- package/ios/BottomSheet/{RNBottomSheetOffsetChangedEvent.m → Event/RNBottomSheetOffsetChangedEvent.mm} +3 -1
- package/ios/BottomSheet/{RNBottomSheetStateChangedEvent.h → Event/RNBottomSheetStateChangedEvent.h} +1 -3
- package/ios/BottomSheet/{RNBottomSheetStateChangedEvent.m → Event/RNBottomSheetStateChangedEvent.mm} +4 -4
- package/ios/BottomSheet/RNBottomSheetComponentView.h +10 -0
- package/ios/BottomSheet/RNBottomSheetComponentView.mm +555 -0
- package/package.json +50 -38
- package/src/BottomSheetNativeComponent.ts +26 -0
- package/src/index.tsx +51 -70
- package/docs/assets/pagerview.gif +0 -0
- package/docs/assets/scrollview.gif +0 -0
- package/docs/assets/struct.png +0 -0
- package/ios/BottomSheet/RNBottomSheet.h +0 -21
- package/ios/BottomSheet/RNBottomSheet.m +0 -407
- package/ios/BottomSheet/RNBottomSheetManager.h +0 -9
- package/ios/BottomSheet/RNBottomSheetManager.m +0 -24
- package/ios/BottomSheet/RNBottomSheetState.h +0 -17
- package/ios/BottomSheet/RNBottomSheetState.m +0 -38
- /package/ios/BottomSheet/{RNBottomSheetOffsetChangedEvent.h → Event/RNBottomSheetOffsetChangedEvent.h} +0 -0
package/src/index.tsx
CHANGED
|
@@ -1,83 +1,64 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
NativeSyntheticEvent,
|
|
4
|
-
requireNativeComponent,
|
|
5
|
-
StyleSheet,
|
|
6
|
-
View,
|
|
7
|
-
ViewProps,
|
|
8
|
-
} from 'react-native';
|
|
2
|
+
import { NativeSyntheticEvent, StyleSheet, View, ViewProps } from 'react-native';
|
|
9
3
|
import splitLayoutProps from './splitLayoutProps';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
progress: number;
|
|
13
|
-
offset: number;
|
|
14
|
-
expandedOffset: number;
|
|
15
|
-
collapsedOffset: number;
|
|
16
|
-
}
|
|
4
|
+
import BottomSheetNativeComponent from './BottomSheetNativeComponent';
|
|
5
|
+
import type { OnStateChangedEventPayload, OnSlideEventPayload } from './BottomSheetNativeComponent';
|
|
17
6
|
|
|
18
7
|
export type BottomSheetState = 'collapsed' | 'expanded' | 'hidden';
|
|
8
|
+
export type BottomSheetOnSlideEvent = NativeSyntheticEvent<OnSlideEventPayload>;
|
|
9
|
+
export type BottomSheetOnStateChangedEvent = NativeSyntheticEvent<OnStateChangedEventPayload>;
|
|
19
10
|
|
|
20
|
-
|
|
21
|
-
|
|
11
|
+
interface BottomSheetProps extends ViewProps {
|
|
12
|
+
onSlide?: (event: BottomSheetOnSlideEvent) => void;
|
|
13
|
+
onStateChanged?: (event: BottomSheetOnStateChangedEvent) => void;
|
|
14
|
+
peekHeight?: number;
|
|
15
|
+
draggable?: boolean;
|
|
16
|
+
state?: BottomSheetState;
|
|
17
|
+
fitToContents?: boolean;
|
|
18
|
+
contentContainerStyle?: ViewProps['style'];
|
|
22
19
|
}
|
|
23
20
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
21
|
+
function BottomSheet(props: BottomSheetProps) {
|
|
22
|
+
const {
|
|
23
|
+
style,
|
|
24
|
+
contentContainerStyle,
|
|
25
|
+
children,
|
|
26
|
+
peekHeight = 200,
|
|
27
|
+
draggable = true,
|
|
28
|
+
state = 'collapsed',
|
|
29
|
+
fitToContents,
|
|
30
|
+
...rest
|
|
31
|
+
} = props;
|
|
32
|
+
const { outer, inner } = splitLayoutProps(StyleSheet.flatten(style));
|
|
33
|
+
return (
|
|
34
|
+
<BottomSheetNativeComponent
|
|
35
|
+
style={[StyleSheet.absoluteFill, outer]}
|
|
36
|
+
peekHeight={peekHeight}
|
|
37
|
+
draggable={draggable}
|
|
38
|
+
status={state}
|
|
39
|
+
{...rest}
|
|
40
|
+
>
|
|
41
|
+
<View
|
|
42
|
+
style={[
|
|
43
|
+
fitToContents ? styles.fitToContents : StyleSheet.absoluteFill,
|
|
44
|
+
inner,
|
|
45
|
+
contentContainerStyle,
|
|
46
|
+
]}
|
|
47
|
+
collapsable={false}
|
|
48
|
+
>
|
|
49
|
+
{children}
|
|
50
|
+
</View>
|
|
51
|
+
</BottomSheetNativeComponent>
|
|
52
|
+
);
|
|
31
53
|
}
|
|
32
54
|
|
|
33
|
-
type BottomSheetProps = NativeBottomSheetProps & {
|
|
34
|
-
fitToContents?: boolean;
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const NativeBottomSheet = requireNativeComponent<NativeBottomSheetProps>('BottomSheet');
|
|
38
|
-
|
|
39
|
-
type NativeBottomSheetInstance = InstanceType<typeof NativeBottomSheet>;
|
|
40
|
-
|
|
41
|
-
const BottomSheet = React.forwardRef<NativeBottomSheetInstance, BottomSheetProps>((props, ref) => {
|
|
42
|
-
const {
|
|
43
|
-
style,
|
|
44
|
-
contentContainerStyle,
|
|
45
|
-
children,
|
|
46
|
-
peekHeight = 200,
|
|
47
|
-
draggable = true,
|
|
48
|
-
state = 'collapsed',
|
|
49
|
-
fitToContents,
|
|
50
|
-
...rest
|
|
51
|
-
} = props;
|
|
52
|
-
const {outer, inner} = splitLayoutProps(StyleSheet.flatten(style));
|
|
53
|
-
return (
|
|
54
|
-
<NativeBottomSheet
|
|
55
|
-
style={[StyleSheet.absoluteFill, outer]}
|
|
56
|
-
peekHeight={peekHeight}
|
|
57
|
-
draggable={draggable}
|
|
58
|
-
state={state}
|
|
59
|
-
{...rest}
|
|
60
|
-
ref={ref}>
|
|
61
|
-
<View
|
|
62
|
-
style={[
|
|
63
|
-
fitToContents ? styles.fitToContents : StyleSheet.absoluteFill,
|
|
64
|
-
inner,
|
|
65
|
-
contentContainerStyle,
|
|
66
|
-
]}
|
|
67
|
-
collapsable={false}>
|
|
68
|
-
{children}
|
|
69
|
-
</View>
|
|
70
|
-
</NativeBottomSheet>
|
|
71
|
-
);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
55
|
const styles = StyleSheet.create({
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
56
|
+
fitToContents: {
|
|
57
|
+
position: 'absolute',
|
|
58
|
+
left: 0,
|
|
59
|
+
right: 0,
|
|
60
|
+
bottom: 0,
|
|
61
|
+
},
|
|
81
62
|
});
|
|
82
63
|
|
|
83
64
|
export default BottomSheet;
|
|
Binary file
|
|
Binary file
|
package/docs/assets/struct.png
DELETED
|
Binary file
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
#import <UIKit/UIKit.h>
|
|
2
|
-
#import <React/RCTView.h>
|
|
3
|
-
#import <React/RCTEventDispatcher.h>
|
|
4
|
-
|
|
5
|
-
#import "RNBottomSheetState.h"
|
|
6
|
-
|
|
7
|
-
NS_ASSUME_NONNULL_BEGIN
|
|
8
|
-
|
|
9
|
-
@interface RNBottomSheet : RCTView
|
|
10
|
-
|
|
11
|
-
@property(nonatomic, copy) RCTDirectEventBlock onSlide;
|
|
12
|
-
@property(nonatomic, copy) RCTDirectEventBlock onStateChanged;
|
|
13
|
-
@property(nonatomic, assign) CGFloat peekHeight;
|
|
14
|
-
@property(nonatomic, assign) RNBottomSheetState state;
|
|
15
|
-
@property(nonatomic, assign) BOOL draggable;
|
|
16
|
-
|
|
17
|
-
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher;
|
|
18
|
-
|
|
19
|
-
@end
|
|
20
|
-
|
|
21
|
-
NS_ASSUME_NONNULL_END
|
|
@@ -1,407 +0,0 @@
|
|
|
1
|
-
#import "RNBottomSheet.h"
|
|
2
|
-
#import "RNBottomSheetStateChangedEvent.h"
|
|
3
|
-
#import "RNBottomSheetOffsetChangedEvent.h"
|
|
4
|
-
|
|
5
|
-
#import <React/UIView+React.h>
|
|
6
|
-
#import <React/RCTRootContentView.h>
|
|
7
|
-
#import <React/RCTTouchHandler.h>
|
|
8
|
-
#import <React/RCTLog.h>
|
|
9
|
-
|
|
10
|
-
@interface RNBottomSheet () <UIGestureRecognizerDelegate>
|
|
11
|
-
|
|
12
|
-
@property(nonatomic, strong) UIView *contentView;
|
|
13
|
-
|
|
14
|
-
@property(nonatomic, strong) UIScrollView *target;
|
|
15
|
-
@property(nonatomic, strong) UIPanGestureRecognizer *panGestureRecognizer;
|
|
16
|
-
|
|
17
|
-
@property(nonatomic, assign) CGFloat minY;
|
|
18
|
-
@property(nonatomic, assign) CGFloat maxY;
|
|
19
|
-
|
|
20
|
-
@property(nonatomic, assign) BOOL nextReturn;
|
|
21
|
-
|
|
22
|
-
@property(nonatomic, strong) CADisplayLink *displayLink;
|
|
23
|
-
@property(nonatomic, strong) RCTEventDispatcher *eventDispatcher;
|
|
24
|
-
|
|
25
|
-
@property(nonatomic, assign) RNBottomSheetState finalState;
|
|
26
|
-
|
|
27
|
-
@property(nonatomic, strong) UIViewPropertyAnimator *animator;
|
|
28
|
-
|
|
29
|
-
@end
|
|
30
|
-
|
|
31
|
-
@implementation RNBottomSheet {
|
|
32
|
-
__weak UIView *_rootView;
|
|
33
|
-
BOOL _isInitialRender;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher {
|
|
37
|
-
if (self = [super init]) {
|
|
38
|
-
_panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
|
|
39
|
-
_panGestureRecognizer.delegate = self;
|
|
40
|
-
_state = RNBottomSheetStateCollapsed;
|
|
41
|
-
_finalState = RNBottomSheetStateCollapsed;
|
|
42
|
-
_eventDispatcher = eventDispatcher;
|
|
43
|
-
_isInitialRender = YES;
|
|
44
|
-
}
|
|
45
|
-
return self;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex {
|
|
49
|
-
[super insertReactSubview:subview atIndex:atIndex];
|
|
50
|
-
if (atIndex == 0) {
|
|
51
|
-
self.contentView = subview;
|
|
52
|
-
[subview addGestureRecognizer:_panGestureRecognizer];
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
- (void)removeReactSubview:(UIView *)subview {
|
|
57
|
-
[super removeReactSubview:subview];
|
|
58
|
-
if (self.contentView == subview) {
|
|
59
|
-
[subview removeGestureRecognizer:_panGestureRecognizer];
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
|
|
64
|
-
if (gestureRecognizer == self.panGestureRecognizer && otherGestureRecognizer == self.target.panGestureRecognizer) {
|
|
65
|
-
return YES;
|
|
66
|
-
}
|
|
67
|
-
return NO;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
|
|
71
|
-
if (gestureRecognizer == self.panGestureRecognizer) {
|
|
72
|
-
if ([super gestureRecognizerShouldBegin:gestureRecognizer]) {
|
|
73
|
-
[self cancelRootViewTouches];
|
|
74
|
-
return YES;
|
|
75
|
-
}
|
|
76
|
-
return NO;
|
|
77
|
-
}
|
|
78
|
-
return [super gestureRecognizerShouldBegin:gestureRecognizer];
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
|
|
82
|
-
if (gestureRecognizer != self.panGestureRecognizer) {
|
|
83
|
-
return YES;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (self.target) {
|
|
87
|
-
[self.target removeObserver:self forKeyPath:@"contentOffset"];
|
|
88
|
-
}
|
|
89
|
-
self.target = nil;
|
|
90
|
-
|
|
91
|
-
UIView *touchView = touch.view;
|
|
92
|
-
|
|
93
|
-
while (touchView != nil && [touchView isDescendantOfView:self]) {
|
|
94
|
-
if ([touchView isKindOfClass:[UIScrollView class]]) {
|
|
95
|
-
UIScrollView *scrollView = (UIScrollView *)touchView;
|
|
96
|
-
if (![self isHorizontal:scrollView]) {
|
|
97
|
-
self.target = scrollView;
|
|
98
|
-
[self.target addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
|
|
99
|
-
break;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
touchView = touchView.superview;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return YES;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
- (void)willMoveToSuperview:(UIView *)superview {
|
|
109
|
-
[super willMoveToSuperview:superview];
|
|
110
|
-
if (superview == nil && self.target) {
|
|
111
|
-
[self.target removeObserver:self forKeyPath:@"contentOffset"];
|
|
112
|
-
self.target = nil;
|
|
113
|
-
}
|
|
114
|
-
if (superview == nil) {
|
|
115
|
-
[self stopWatchBottomSheetTransition];
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
- (void)willMoveToWindow:(UIWindow *)newWindow {
|
|
120
|
-
[super willMoveToWindow:newWindow];
|
|
121
|
-
if (newWindow == nil) {
|
|
122
|
-
[self stopWatchBottomSheetTransition];
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
- (void)didMoveToWindow {
|
|
127
|
-
[super didMoveToWindow];
|
|
128
|
-
if (self.window) {
|
|
129
|
-
[self cacheRootView];
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
- (void)cacheRootView {
|
|
134
|
-
UIView *rootView = self;
|
|
135
|
-
while (rootView.superview && ![rootView isReactRootView]) {
|
|
136
|
-
rootView = rootView.superview;
|
|
137
|
-
}
|
|
138
|
-
_rootView = rootView;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
- (void)cancelRootViewTouches {
|
|
142
|
-
RCTRootContentView *rootView = (RCTRootContentView *)_rootView;
|
|
143
|
-
[rootView.touchHandler cancel];
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
- (BOOL)isHorizontal:(UIScrollView *)scrollView {
|
|
147
|
-
return scrollView.contentSize.width > self.frame.size.width;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
- (void)reactSetFrame:(CGRect)frame {
|
|
152
|
-
[super reactSetFrame:frame];
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
- (void)layoutSubviews {
|
|
156
|
-
[super layoutSubviews];
|
|
157
|
-
if (!self.contentView) {
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
if (!CGRectEqualToRect(self.contentView.frame, CGRectZero)) {
|
|
162
|
-
[self calculateOffset];
|
|
163
|
-
if (self.state == RNBottomSheetStateCollapsed) {
|
|
164
|
-
self.contentView.frame = CGRectOffset(self.contentView.frame, 0, self.maxY - self.contentView.frame.origin.y);
|
|
165
|
-
} else if (self.state == RNBottomSheetStateExpanded) {
|
|
166
|
-
self.contentView.frame = CGRectOffset(self.contentView.frame, 0, self.minY - self.contentView.frame.origin.y);
|
|
167
|
-
} else if (self.state == RNBottomSheetStateHidden) {
|
|
168
|
-
self.contentView.frame = CGRectOffset(self.contentView.frame, 0, self.frame.size.height - self.contentView.frame.origin.y);
|
|
169
|
-
}
|
|
170
|
-
[self dispatchOnSlide:self.contentView.frame.origin.y];
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
174
|
-
if (self.finalState == RNBottomSheetStateExpanded && self->_isInitialRender) {
|
|
175
|
-
[self settleToState:self.finalState withFling:YES];
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
self->_isInitialRender = NO;
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
- (void)calculateOffset {
|
|
183
|
-
CGFloat parentHeight = self.frame.size.height;
|
|
184
|
-
self.minY = fmax(0, parentHeight - self.contentView.frame.size.height);
|
|
185
|
-
self.maxY = fmax(self.minY, parentHeight - self.peekHeight);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
- (void)handlePan:(UIPanGestureRecognizer *)pan {
|
|
189
|
-
if (!self.draggable || self.state == RNBottomSheetStateSettling) {
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
CGFloat translationY = [pan translationInView:self.contentView].y;
|
|
194
|
-
[pan setTranslation:CGPointZero inView:self.contentView];
|
|
195
|
-
|
|
196
|
-
CGFloat top = self.contentView.frame.origin.y;
|
|
197
|
-
|
|
198
|
-
if (pan.state == UIGestureRecognizerStateChanged) {
|
|
199
|
-
[self setStateInternal:RNBottomSheetStateDragging];
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// 如果有嵌套滚动
|
|
203
|
-
if (self.target) {
|
|
204
|
-
if(translationY > 0 && top < self.maxY && self.target.contentOffset.y <= 0) {
|
|
205
|
-
//向下拖
|
|
206
|
-
CGFloat y = fmin(top + translationY, self.maxY);
|
|
207
|
-
self.contentView.frame = CGRectOffset(self.contentView.frame, 0, y - top);
|
|
208
|
-
[self dispatchOnSlide:self.contentView.frame.origin.y];
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
if (translationY < 0 && top > self.minY) {
|
|
212
|
-
//向上拖
|
|
213
|
-
CGFloat y = fmax(top + translationY, self.minY);
|
|
214
|
-
self.contentView.frame = CGRectOffset(self.contentView.frame, 0, y - top);
|
|
215
|
-
[self dispatchOnSlide:self.contentView.frame.origin.y];
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// 没有嵌套滚动
|
|
220
|
-
if (!self.target) {
|
|
221
|
-
if(translationY > 0 && top < self.maxY) {
|
|
222
|
-
//向下拖
|
|
223
|
-
CGFloat y = fmin(top + translationY, self.maxY);
|
|
224
|
-
self.contentView.frame = CGRectOffset(self.contentView.frame, 0, y - top);
|
|
225
|
-
[self dispatchOnSlide:self.contentView.frame.origin.y];
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
if (translationY < 0 && top > self.minY) {
|
|
229
|
-
//向上拖
|
|
230
|
-
CGFloat y = fmax(top + translationY, self.minY);
|
|
231
|
-
self.contentView.frame = CGRectOffset(self.contentView.frame, 0, y - top);
|
|
232
|
-
[self dispatchOnSlide:self.contentView.frame.origin.y];
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
if (pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateCancelled) {
|
|
237
|
-
// RCTLogInfo(@"velocity:%f", [pan velocityInView:self.contentView].y);
|
|
238
|
-
CGFloat velocity = [pan velocityInView:self.contentView].y;
|
|
239
|
-
if (velocity > 400) {
|
|
240
|
-
if (self.target && self.target.contentOffset.y <= 0) {
|
|
241
|
-
//如果是类似轻扫的那种
|
|
242
|
-
[self settleToState:RNBottomSheetStateCollapsed withFling:YES];
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if (!self.target) {
|
|
246
|
-
//如果是类似轻扫的那种
|
|
247
|
-
[self settleToState:RNBottomSheetStateCollapsed withFling:YES];
|
|
248
|
-
}
|
|
249
|
-
} else if (velocity < -400) {
|
|
250
|
-
//如果是类似轻扫的那种
|
|
251
|
-
[self settleToState:RNBottomSheetStateExpanded withFling:YES];
|
|
252
|
-
} else {
|
|
253
|
-
//如果是普通拖拽
|
|
254
|
-
if(fabs(self.contentView.frame.origin.y - self.minY) > fabs(self.contentView.frame.origin.y - self.maxY)) {
|
|
255
|
-
[self settleToState:RNBottomSheetStateCollapsed withFling:NO];
|
|
256
|
-
} else {
|
|
257
|
-
[self settleToState:RNBottomSheetStateExpanded withFling:NO];
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(UIScrollView *)target change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
|
|
264
|
-
if (_nextReturn) {
|
|
265
|
-
_nextReturn = false;
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
if (![keyPath isEqualToString:@"contentOffset"]) {
|
|
270
|
-
return;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
CGFloat new = [change[@"new"] CGPointValue].y;
|
|
274
|
-
CGFloat old = [change[@"old"] CGPointValue].y;
|
|
275
|
-
|
|
276
|
-
if (new == old) {
|
|
277
|
-
return;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
CGFloat dy = old - new;
|
|
281
|
-
|
|
282
|
-
if (dy > 0) {
|
|
283
|
-
//向下
|
|
284
|
-
if(target.contentOffset.y < 0){
|
|
285
|
-
_nextReturn = true;
|
|
286
|
-
target.contentOffset = CGPointMake(0, 0);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
if (dy < 0) {
|
|
291
|
-
//向上
|
|
292
|
-
if (self.contentView.frame.origin.y > self.minY) {
|
|
293
|
-
_nextReturn = true;
|
|
294
|
-
target.contentOffset = CGPointMake(0, old);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
- (void)setPeekHeight:(CGFloat)peekHeight {
|
|
300
|
-
_peekHeight = peekHeight;
|
|
301
|
-
if (!CGRectEqualToRect(self.contentView.frame, CGRectZero)) {
|
|
302
|
-
[self calculateOffset];
|
|
303
|
-
if (self.state == RNBottomSheetStateCollapsed) {
|
|
304
|
-
[self settleToState:RNBottomSheetStateCollapsed withFling:NO];
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
- (void)setState:(RNBottomSheetState)state {
|
|
310
|
-
if (_isInitialRender) {
|
|
311
|
-
self.finalState = state;
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
if (self.finalState == state) {
|
|
316
|
-
return;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
self.finalState = state;
|
|
320
|
-
|
|
321
|
-
[self settleToState:state withFling:YES];
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
- (void)settleToState:(RNBottomSheetState)state withFling:(BOOL)fling {
|
|
325
|
-
if (state == RNBottomSheetStateCollapsed) {
|
|
326
|
-
[self settleToState:state top:self.maxY withFling:fling];
|
|
327
|
-
} else if (state == RNBottomSheetStateExpanded) {
|
|
328
|
-
[self settleToState:state top:self.minY withFling:fling];
|
|
329
|
-
} else if (state == RNBottomSheetStateHidden) {
|
|
330
|
-
[self settleToState:state top:self.frame.size.height withFling:fling];
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
- (void)settleToState:(RNBottomSheetState)state top:(CGFloat)top withFling:(BOOL)fling {
|
|
335
|
-
self.target.pagingEnabled = YES;
|
|
336
|
-
[self setStateInternal:RNBottomSheetStateSettling];
|
|
337
|
-
[self startWatchBottomSheetTransition];
|
|
338
|
-
|
|
339
|
-
if (self.animator) {
|
|
340
|
-
[self.animator stopAnimation:YES];
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
UIViewAnimationOptions options = UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseOut;
|
|
344
|
-
|
|
345
|
-
self.animator = [UIViewPropertyAnimator runningPropertyAnimatorWithDuration:0.3 delay:0 options:options animations:^{
|
|
346
|
-
self.contentView.frame = CGRectOffset(self.contentView.frame, 0, top - self.contentView.frame.origin.y);
|
|
347
|
-
} completion:^(UIViewAnimatingPosition finalPosition) {
|
|
348
|
-
self.target.pagingEnabled = NO;
|
|
349
|
-
self.animator = nil;
|
|
350
|
-
[self stopWatchBottomSheetTransition];
|
|
351
|
-
if (self.finalState == state) {
|
|
352
|
-
[self setStateInternal:state];
|
|
353
|
-
}
|
|
354
|
-
}];
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
- (void)setStateInternal:(RNBottomSheetState)state {
|
|
358
|
-
if (_state == state) {
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
_state = state;
|
|
362
|
-
|
|
363
|
-
if (state == RNBottomSheetStateExpanded) {
|
|
364
|
-
[self dispatchOnSlide:self.minY];
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
if (state == RNBottomSheetStateHidden) {
|
|
368
|
-
[self dispatchOnSlide:self.frame.size.height];
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
if (state == RNBottomSheetStateCollapsed) {
|
|
372
|
-
[self dispatchOnSlide:self.maxY];
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
if (state == RNBottomSheetStateCollapsed || state == RNBottomSheetStateExpanded || state == RNBottomSheetStateHidden) {
|
|
376
|
-
[self.eventDispatcher sendEvent:[[RNBottomSheetStateChangedEvent alloc] initWithViewTag:self.reactTag state:state]];
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
- (void)dispatchOnSlide:(CGFloat)top {
|
|
381
|
-
if (top < 0 || self.maxY == 0) {
|
|
382
|
-
return;
|
|
383
|
-
}
|
|
384
|
-
CGFloat progress = fmin((top - self.minY) * 1.0f / (self.maxY - self.minY), 1);
|
|
385
|
-
[self.eventDispatcher sendEvent:[[RNBottomSheetOffsetChangedEvent alloc] initWithViewTag:self.reactTag progress:progress offset:top minY:self.minY maxY:self.maxY]];
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
- (void)startWatchBottomSheetTransition {
|
|
389
|
-
[self stopWatchBottomSheetTransition];
|
|
390
|
-
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(watchBottomSheetTransition)];
|
|
391
|
-
_displayLink.preferredFramesPerSecond = 120;
|
|
392
|
-
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
- (void)stopWatchBottomSheetTransition {
|
|
396
|
-
if(_displayLink){
|
|
397
|
-
[_displayLink invalidate];
|
|
398
|
-
_displayLink = nil;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
- (void)watchBottomSheetTransition {
|
|
403
|
-
CGFloat top = [self.contentView.layer presentationLayer].frame.origin.y;
|
|
404
|
-
[self dispatchOnSlide:top];
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
@end
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
#import "RNBottomSheetManager.h"
|
|
2
|
-
#import "RNBottomSheet.h"
|
|
3
|
-
#import "RNBottomSheetState.h"
|
|
4
|
-
|
|
5
|
-
@implementation RNBottomSheetManager
|
|
6
|
-
|
|
7
|
-
RCT_EXPORT_MODULE(BottomSheet)
|
|
8
|
-
|
|
9
|
-
- (UIView *)view {
|
|
10
|
-
RNBottomSheet *bottomSheet = [[RNBottomSheet alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
|
|
11
|
-
bottomSheet.pointerEvents = RCTPointerEventsBoxNone;
|
|
12
|
-
return bottomSheet;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
RCT_EXPORT_VIEW_PROPERTY(onSlide, RCTDirectEventBlock)
|
|
16
|
-
RCT_EXPORT_VIEW_PROPERTY(onStateChanged, RCTDirectEventBlock)
|
|
17
|
-
RCT_EXPORT_VIEW_PROPERTY(peekHeight, CGFloat)
|
|
18
|
-
RCT_EXPORT_VIEW_PROPERTY(draggable, BOOL)
|
|
19
|
-
|
|
20
|
-
RCT_CUSTOM_VIEW_PROPERTY(state, NSString, RNBottomSheet) {
|
|
21
|
-
view.state = RNBottomSheetStateFromString([RCTConvert NSString:json]);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
@end
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
#import <React/RCTUtils.h>
|
|
2
|
-
|
|
3
|
-
NS_ASSUME_NONNULL_BEGIN
|
|
4
|
-
|
|
5
|
-
typedef NS_ENUM(NSUInteger, RNBottomSheetState) {
|
|
6
|
-
RNBottomSheetStateCollapsed = 0,
|
|
7
|
-
RNBottomSheetStateExpanded,
|
|
8
|
-
RNBottomSheetStateHidden,
|
|
9
|
-
RNBottomSheetStateDragging,
|
|
10
|
-
RNBottomSheetStateSettling,
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
RCT_EXTERN RNBottomSheetState RNBottomSheetStateFromString(NSString *state);
|
|
14
|
-
|
|
15
|
-
RCT_EXTERN NSString* RNBottomSheetStateToString(RNBottomSheetState state);
|
|
16
|
-
|
|
17
|
-
NS_ASSUME_NONNULL_END
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
#import "RNBottomSheetState.h"
|
|
2
|
-
|
|
3
|
-
RNBottomSheetState RNBottomSheetStateFromString(NSString *state) {
|
|
4
|
-
if ([state isEqualToString:@"collapsed"]) {
|
|
5
|
-
return RNBottomSheetStateCollapsed;
|
|
6
|
-
}
|
|
7
|
-
if ([state isEqualToString:@"expanded"]) {
|
|
8
|
-
return RNBottomSheetStateExpanded;
|
|
9
|
-
}
|
|
10
|
-
if ([state isEqualToString:@"settling"]) {
|
|
11
|
-
return RNBottomSheetStateSettling;
|
|
12
|
-
}
|
|
13
|
-
if ([state isEqualToString:@"dragging"]) {
|
|
14
|
-
return RNBottomSheetStateDragging;
|
|
15
|
-
}
|
|
16
|
-
return RNBottomSheetStateHidden;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
NSString* RNBottomSheetStateToString(RNBottomSheetState state) {
|
|
21
|
-
if (state == RNBottomSheetStateCollapsed) {
|
|
22
|
-
return @"collapsed";
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (state == RNBottomSheetStateExpanded) {
|
|
26
|
-
return @"expanded";
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (state == RNBottomSheetStateSettling) {
|
|
30
|
-
return @"settling";
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (state == RNBottomSheetStateDragging) {
|
|
34
|
-
return @"dragging";
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return @"hidden";
|
|
38
|
-
}
|
|
File without changes
|