Permalink
Browse files

UIRefreshControl added to scroll view

Summary: **What:**

adds `onRefreshStart` property to `ScrollView.js` for displaying and activating pull to refresh.

**Why:**

Javascript implementations seemed a little flakey and inconsistent.  As you can see in the issues below:

#2356
#745

So this is an attempt a completely native implementation.

What do you think?

![Image of dog](http://i.imgur.com/HcTQnzJ.gif)
Closes #4205

Reviewed By: svcscm

Differential Revision: D2674945

Pulled By: nicklockwood

fb-gh-sync-id: 65113a5db9785df5a95c68323c2cdf19f3b217b1
  • Loading branch information...
EwanThomas authored and facebook-github-bot-4 committed Nov 19, 2015
1 parent 5950f8c commit 2faf8632d350c1ecb85f20d99eabf8d48202fc82
@@ -15,6 +15,7 @@ var EdgeInsetsPropType = require('EdgeInsetsPropType');
var Platform = require('Platform');
var PointPropType = require('PointPropType');
var RCTScrollView = require('NativeModules').UIManager.RCTScrollView;
+var RCTScrollViewManager = require('NativeModules').ScrollViewManager;
var React = require('React');
var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
var RCTUIManager = require('NativeModules').UIManager;
@@ -279,6 +280,21 @@ var ScrollView = React.createClass({
* @platform ios
*/
zoomScale: PropTypes.number,
+
+ /**
+ * When defined, displays a UIRefreshControl.
+ * Invoked with a function to stop refreshing when the UIRefreshControl is animating.
+ *
+ * ```
+ * (endRefreshing) => {
+ * endRefreshing();
+ * }
+ * ```
+ *
+ * @platform ios
+ */
+ onRefreshStart: PropTypes.func,
+
},
mixins: [ScrollResponder.Mixin],
@@ -291,6 +307,12 @@ var ScrollView = React.createClass({
this.refs[SCROLLVIEW].setNativeProps(props);
},
+ endRefreshing: function() {
+ RCTScrollViewManager.endRefreshing(
+ React.findNodeHandle(this)
+ );
+ },
+
/**
* Returns a reference to the underlying scroll responder, which supports
* operations like `scrollTo`. All ScrollView-like components should
@@ -396,6 +418,13 @@ var ScrollView = React.createClass({
onResponderReject: this.scrollResponderHandleResponderReject,
};
+ var onRefreshStart = this.props.onRefreshStart;
+ // this is necessary because if we set it on props, even when empty,
+ // it'll trigger the default pull-to-refresh behaviour on native.
+ props.onRefreshStart = onRefreshStart
+ ? function() { onRefreshStart && onRefreshStart(this.endRefreshing); }.bind(this)
+ : null;
+
var ScrollViewClass;
if (Platform.OS === 'ios') {
ScrollViewClass = RCTScrollView;
@@ -47,6 +47,9 @@
@property (nonatomic, assign) int snapToInterval;
@property (nonatomic, copy) NSString *snapToAlignment;
@property (nonatomic, copy) NSIndexSet *stickyHeaderIndices;
+@property (nonatomic, copy) RCTDirectEventBlock onRefreshStart;
+
+- (void)endRefreshing;
@end
@@ -144,6 +144,7 @@ @interface RCTCustomScrollView : UIScrollView<UIGestureRecognizerDelegate>
@property (nonatomic, copy) NSIndexSet *stickyHeaderIndices;
@property (nonatomic, assign) BOOL centerContent;
+@property (nonatomic, strong) UIRefreshControl *refreshControl;
@end
@@ -352,6 +353,15 @@ - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
return hitView ?: [super hitTest:point withEvent:event];
}
+- (void)setRefreshControl:(UIRefreshControl *)refreshControl
+{
+ if (_refreshControl) {
+ [_refreshControl removeFromSuperview];
+ }
+ _refreshControl = refreshControl;
+ [self addSubview:_refreshControl];
+}
+
@end
@implementation RCTScrollView
@@ -844,6 +854,34 @@ - (id)valueForUndefinedKey:(NSString *)key
return [_scrollView valueForKey:key];
}
+- (void)setOnRefreshStart:(RCTDirectEventBlock)onRefreshStart
+{
+ if (!onRefreshStart) {
+ _onRefreshStart = nil;
+ _scrollView.refreshControl = nil;
+ return;
+ }
+ _onRefreshStart = [onRefreshStart copy];
+
+ if (!_scrollView.refreshControl) {
+ UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
+ [refreshControl addTarget:self action:@selector(refreshControlValueChanged) forControlEvents:UIControlEventValueChanged];
+ _scrollView.refreshControl = refreshControl;
+ }
+}
+
+- (void)refreshControlValueChanged
+{
+ if (self.onRefreshStart) {
+ self.onRefreshStart(nil);
+ }
+}
+
+- (void)endRefreshing
+{
+ [_scrollView.refreshControl endRefreshing];
+}
+
@end
@implementation RCTEventDispatcher (RCTScrollView)
@@ -65,6 +65,7 @@ - (UIView *)view
RCT_EXPORT_VIEW_PROPERTY(snapToInterval, int)
RCT_EXPORT_VIEW_PROPERTY(snapToAlignment, NSString)
RCT_REMAP_VIEW_PROPERTY(contentOffset, scrollView.contentOffset, CGPoint)
+RCT_EXPORT_VIEW_PROPERTY(onRefreshStart, RCTDirectEventBlock)
- (NSDictionary<NSString *, id> *)constantsToExport
{
@@ -114,6 +115,22 @@ - (UIView *)view
}];
}
+RCT_EXPORT_METHOD(endRefreshing:(nonnull NSNumber *)reactTag)
+{
+ [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTScrollView *> *viewRegistry) {
+
+ RCTScrollView *view = viewRegistry[reactTag];
+ if (!view || ![view isKindOfClass:[RCTScrollView class]]) {
+ RCTLogError(@"Cannot find RCTScrollView with tag #%@", reactTag);
+ return;
+ }
+
+ [view endRefreshing];
+
+ }];
+}
+
+
- (NSArray<NSString *> *)customDirectEventTypes
{
return @[
@@ -127,3 +144,4 @@ - (UIView *)view
}
@end
+

4 comments on commit 2faf863

@qingfeng

This comment has been minimized.

Show comment
Hide comment
@qingfeng

qingfeng Nov 23, 2015

Contributor

Cool

Contributor

qingfeng replied Nov 23, 2015

Cool

@exentrich

This comment has been minimized.

Show comment
Hide comment
@exentrich

exentrich Nov 29, 2015

Contributor

Great!

Contributor

exentrich replied Nov 29, 2015

Great!

@iZaL

This comment has been minimized.

Show comment
Hide comment

iZaL replied Dec 24, 2015

👍

@david-a

This comment has been minimized.

Show comment
Hide comment
@david-a

david-a Dec 30, 2015

@EwanThomas Great feature! Thanks!
I saw in #4205 the discussion about adding styling capability to the refresh control (either by adding ios specific properties like tintColor or letting the user assign his own UIRefreshControl to the ScrollView).

Maybe I missed it somehow, I just wanted to ask what was the conclusion? and also are you going to implement such interface for styling?

@EwanThomas Great feature! Thanks!
I saw in #4205 the discussion about adding styling capability to the refresh control (either by adding ios specific properties like tintColor or letting the user assign his own UIRefreshControl to the ScrollView).

Maybe I missed it somehow, I just wanted to ask what was the conclusion? and also are you going to implement such interface for styling?

Please sign in to comment.