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