From 6aca514b3a6e94a771708188c056743d84f0654c Mon Sep 17 00:00:00 2001 From: Rostislav Simonik Date: Fri, 15 Feb 2019 02:36:47 -0800 Subject: [PATCH] Add fix for refresh control state's race condition. (#21763) Summary: Fixes #21762 Pull Request resolved: https://github.com/facebook/react-native/pull/21763 Differential Revision: D14064621 Pulled By: cpojer fbshipit-source-id: 63010248a46cb49ed17ed89d7c55945aa7b22973 --- React/Views/RCTRefreshControl.m | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/React/Views/RCTRefreshControl.m b/React/Views/RCTRefreshControl.m index b26bb03371ed56..24090c83e41011 100644 --- a/React/Views/RCTRefreshControl.m +++ b/React/Views/RCTRefreshControl.m @@ -12,6 +12,8 @@ @implementation RCTRefreshControl { BOOL _isInitialRender; BOOL _currentRefreshingState; + UInt64 _currentRefreshingStateClock; + UInt64 _currentRefreshingStateTimestamp; BOOL _refreshingProgrammatically; NSString *_title; UIColor *_titleColor; @@ -21,6 +23,8 @@ - (instancetype)init { if ((self = [super init])) { [self addTarget:self action:@selector(refreshControlValueChanged) forControlEvents:UIControlEventValueChanged]; + _currentRefreshingStateClock = 1; + _currentRefreshingStateTimestamp = 0; _isInitialRender = true; _currentRefreshingState = false; } @@ -49,6 +53,7 @@ - (void)layoutSubviews - (void)beginRefreshingProgrammatically { + UInt64 beginRefreshingTimestamp = _currentRefreshingStateTimestamp; _refreshingProgrammatically = YES; // When using begin refreshing we need to adjust the ScrollView content offset manually. UIScrollView *scrollView = (UIScrollView *)self.superview; @@ -62,7 +67,10 @@ - (void)beginRefreshingProgrammatically animations:^(void) { [scrollView setContentOffset:offset]; } completion:^(__unused BOOL finished) { - [super beginRefreshing]; + if(beginRefreshingTimestamp == self->_currentRefreshingStateTimestamp) { + [super beginRefreshing]; + [self setCurrentRefreshingState:super.refreshing]; + } }]; } @@ -72,6 +80,7 @@ - (void)endRefreshingProgrammatically // endRefreshing otherwise the next pull to refresh will not work properly. UIScrollView *scrollView = (UIScrollView *)self.superview; if (_refreshingProgrammatically && scrollView.contentOffset.y < 0) { + UInt64 endRefreshingTimestamp = _currentRefreshingStateTimestamp; CGPoint offset = {scrollView.contentOffset.x, 0}; [UIView animateWithDuration:0.25 delay:0 @@ -79,7 +88,10 @@ - (void)endRefreshingProgrammatically animations:^(void) { [scrollView setContentOffset:offset]; } completion:^(__unused BOOL finished) { - [super endRefreshing]; + if(endRefreshingTimestamp == self->_currentRefreshingStateTimestamp) { + [super endRefreshing]; + [self setCurrentRefreshingState:super.refreshing]; + } }]; } else { [super endRefreshing]; @@ -120,7 +132,7 @@ - (void)_updateTitle - (void)setRefreshing:(BOOL)refreshing { if (_currentRefreshingState != refreshing) { - _currentRefreshingState = refreshing; + [self setCurrentRefreshingState:refreshing]; if (refreshing) { if (!_isInitialRender) { @@ -132,9 +144,15 @@ - (void)setRefreshing:(BOOL)refreshing } } +- (void)setCurrentRefreshingState:(BOOL)refreshing +{ + _currentRefreshingState = refreshing; + _currentRefreshingStateTimestamp = _currentRefreshingStateClock++; +} + - (void)refreshControlValueChanged { - _currentRefreshingState = super.refreshing; + [self setCurrentRefreshingState:super.refreshing]; _refreshingProgrammatically = NO; if (_onRefresh) {