From 3e1f1ea7bbb0461e8ad5a40cb29e94c46cddc04d Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Sun, 7 Feb 2016 13:39:17 -0800 Subject: [PATCH] Allows RefreshControl to be mounted with refreshing = true Summary: RefreshControl did not start refreshing when refreshing was set to true initially. It also did not start refreshing on iOS when setting the prop from false to true without doing a pull to refresh gesture. This was a pain in the ass to make work on iOS because UIRefreshControl seems super sensitive to when beginRefreshing can be called, for the initial render I need to call it in layoutSubviews. I also have to manually adjust the scrollview content offset when calling beginRefreshing. The code is a bit hacky but it was the only solution I found that was actually working. Fixes #5716 Closes https://github.com/facebook/react-native/pull/5745 Reviewed By: svcscm Differential Revision: D2910716 Pulled By: nicklockwood fb-gh-sync-id: d60e73bcfe8d86bb01249ba5f17e6a23c5a5aff6 --- React/Views/RCTRefreshControl.m | 53 +++++++++++++++++-- .../SwipeRefreshLayoutManager.java | 11 +++- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/React/Views/RCTRefreshControl.m b/React/Views/RCTRefreshControl.m index d3fc0e7f9..b066f1b63 100644 --- a/React/Views/RCTRefreshControl.m +++ b/React/Views/RCTRefreshControl.m @@ -11,18 +11,59 @@ #import "RCTUtils.h" -@implementation RCTRefreshControl +@implementation RCTRefreshControl { + BOOL _initialRefreshingState; + BOOL _isInitialRender; +} - (instancetype)init { if ((self = [super init])) { [self addTarget:self action:@selector(refreshControlValueChanged) forControlEvents:UIControlEventValueChanged]; + _isInitialRender = true; } return self; } RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) +- (void)layoutSubviews +{ + [super layoutSubviews]; + + // If the control is refreshing when mounted we need to call + // beginRefreshing in layoutSubview or it doesn't work. + if (_isInitialRender && _initialRefreshingState) { + [self beginRefreshing]; + } + _isInitialRender = false; +} + +- (void)beginRefreshing +{ + // When using begin refreshing we need to adjust the ScrollView content offset manually. + UIScrollView *scrollView = (UIScrollView *)self.superview; + CGPoint offset = {scrollView.contentOffset.x, scrollView.contentOffset.y - self.frame.size.height}; + // Don't animate when the prop is set initialy. + if (_isInitialRender) { + // Must use `[scrollView setContentOffset:offset animated:NO]` instead of just setting + // `scrollview.contentOffset` or it doesn't work, don't ask me why! + [scrollView setContentOffset:offset animated:NO]; + [super beginRefreshing]; + } else { + // `beginRefreshing` must be called after the animation is done. This is why it is impossible + // to use `setContentOffset` with `animated:YES`. + [UIView animateWithDuration:0.25 + delay:0 + options:UIViewAnimationOptionBeginFromCurrentState + animations:^(void) { + [scrollView setContentOffset:offset]; + } completion:^(__unused BOOL finished) { + [super beginRefreshing]; + }]; + } +} + - (NSString *)title { return self.attributedTitle.string; @@ -35,9 +76,15 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) - (void)setRefreshing:(BOOL)refreshing { - if (super.refreshing != refreshing) { + if (self.refreshing != refreshing) { if (refreshing) { - [self beginRefreshing]; + // If it is the initial render, beginRefreshing will get called + // in layoutSubviews. + if (_isInitialRender) { + _initialRefreshingState = refreshing; + } else { + [self beginRefreshing]; + } } else { [self endRefreshing]; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java index 3dccc4c48..07a42e049 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java @@ -71,8 +71,15 @@ public class SwipeRefreshLayoutManager extends ViewGroupManager