From 310a6bcf4ba7ca162d3ba1c03e0ab07ff41f9ead Mon Sep 17 00:00:00 2001 From: David Biedenbach Date: Thu, 11 Mar 2021 10:22:16 -0800 Subject: [PATCH] Fix Issue 10718: Add iOS support for progressViewOffset (#30737) Summary: Fixes https://github.com/facebook/react-native/issues/10718, bringing `progressViewOffset` support to iOS. Thanks to Taylor123 for the initial PR upon which this fix is based. ## Changelog [iOS] [Fix] - `progressViewOffset` prop of `RefreshControl` and `VirtualizedList` now works on iOS Pull Request resolved: https://github.com/facebook/react-native/pull/30737 Test Plan: Tested with quick-and-dirty sample app. ![progressViewOffset-iOS](https://user-images.githubusercontent.com/1563532/104526540-82fe1d80-55b7-11eb-9f99-e025bedf4874.gif) ## Documentation The corresponding documentation update PR can be found [here](https://github.com/facebook/react-native-website/pull/2441). Reviewed By: kacieb Differential Revision: D26813977 Pulled By: sammy-SC fbshipit-source-id: 45cc5a647d70e44a29c6391b7586cb41ca011bef --- .../PullToRefreshViewNativeComponent.js | 10 ++++++++- .../RefreshControl/RefreshControl.js | 10 ++++----- Libraries/Lists/VirtualizedList.js | 1 - .../Views/RefreshControl/RCTRefreshControl.m | 21 +++++++++++++++++++ .../RefreshControl/RCTRefreshControlManager.m | 1 + 5 files changed, 36 insertions(+), 7 deletions(-) diff --git a/Libraries/Components/RefreshControl/PullToRefreshViewNativeComponent.js b/Libraries/Components/RefreshControl/PullToRefreshViewNativeComponent.js index 4947b1e31df3b3..fa4791f2ab620c 100644 --- a/Libraries/Components/RefreshControl/PullToRefreshViewNativeComponent.js +++ b/Libraries/Components/RefreshControl/PullToRefreshViewNativeComponent.js @@ -8,7 +8,11 @@ * @flow strict-local */ -import type {DirectEventHandler, WithDefault} from '../../Types/CodegenTypes'; +import type { + DirectEventHandler, + Float, + WithDefault, +} from '../../Types/CodegenTypes'; import type {ColorValue} from '../../StyleSheet/StyleSheet'; import type {ViewProps} from '../View/ViewPropTypes'; import * as React from 'react'; @@ -32,6 +36,10 @@ type NativeProps = $ReadOnly<{| * The title displayed under the refresh indicator. */ title?: WithDefault, + /** + * Progress view top offset + */ + progressViewOffset?: WithDefault, /** * Called when the view starts refreshing. diff --git a/Libraries/Components/RefreshControl/RefreshControl.js b/Libraries/Components/RefreshControl/RefreshControl.js index 48bafb5ca62c83..0f0a16dbbccfc5 100644 --- a/Libraries/Components/RefreshControl/RefreshControl.js +++ b/Libraries/Components/RefreshControl/RefreshControl.js @@ -52,10 +52,6 @@ type AndroidProps = $ReadOnly<{| * Size of the refresh indicator. */ size?: ?('default' | 'large'), - /** - * Progress view top offset - */ - progressViewOffset?: ?number, |}>; export type RefreshControlProps = $ReadOnly<{| @@ -72,6 +68,11 @@ export type RefreshControlProps = $ReadOnly<{| * Whether the view should be indicating an active refresh. */ refreshing: boolean, + + /** + * Progress view top offset + */ + progressViewOffset?: ?number, |}>; /** @@ -162,7 +163,6 @@ class RefreshControl extends React.Component { colors, progressBackgroundColor, size, - progressViewOffset, ...props } = this.props; return ( diff --git a/Libraries/Lists/VirtualizedList.js b/Libraries/Lists/VirtualizedList.js index 7daac9f077f20e..dcf4dd50b99f53 100644 --- a/Libraries/Lists/VirtualizedList.js +++ b/Libraries/Lists/VirtualizedList.js @@ -246,7 +246,6 @@ type OptionalProps = {| persistentScrollbar?: ?boolean, /** * Set this when offset is needed for the loading indicator to show correctly. - * @platform android */ progressViewOffset?: number, /** diff --git a/React/Views/RefreshControl/RCTRefreshControl.m b/React/Views/RefreshControl/RCTRefreshControl.m index 6985c38bce40b2..91b0e42e3d773e 100644 --- a/React/Views/RefreshControl/RCTRefreshControl.m +++ b/React/Views/RefreshControl/RCTRefreshControl.m @@ -21,6 +21,7 @@ @implementation RCTRefreshControl { BOOL _refreshingProgrammatically; NSString *_title; UIColor *_titleColor; + CGFloat _progressViewOffset; } - (instancetype)init @@ -40,6 +41,7 @@ - (instancetype)init - (void)layoutSubviews { [super layoutSubviews]; + [self _applyProgressViewOffset]; // If the control is refreshing when mounted we need to call // beginRefreshing in layoutSubview or it doesn't work. @@ -120,6 +122,19 @@ - (void)endRefreshingProgrammatically } } +- (void)_applyProgressViewOffset +{ + // progressViewOffset must be converted from the ScrollView parent's coordinate space to + // the coordinate space of the RefreshControl. This ensures that the control respects any + // offset in the view hierarchy, and that progressViewOffset is not inadvertently applied + // multiple times. + UIView *scrollView = self.superview; + UIView *target = scrollView.superview; + CGPoint rawOffset = CGPointMake(0, _progressViewOffset); + CGPoint converted = [self convertPoint:rawOffset fromView:target]; + self.frame = CGRectOffset(self.frame, 0, converted.y); +} + - (NSString *)title { return _title; @@ -172,6 +187,12 @@ - (void)setCurrentRefreshingState:(BOOL)refreshing _currentRefreshingStateTimestamp = _currentRefreshingStateClock++; } +- (void)setProgressViewOffset:(CGFloat)offset +{ + _progressViewOffset = offset; + [self _applyProgressViewOffset]; +} + - (void)refreshControlValueChanged { [self setCurrentRefreshingState:super.refreshing]; diff --git a/React/Views/RefreshControl/RCTRefreshControlManager.m b/React/Views/RefreshControl/RCTRefreshControlManager.m index 3152b1c8069020..87c997a8edda4f 100644 --- a/React/Views/RefreshControl/RCTRefreshControlManager.m +++ b/React/Views/RefreshControl/RCTRefreshControlManager.m @@ -25,6 +25,7 @@ - (UIView *)view RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor) RCT_EXPORT_VIEW_PROPERTY(title, NSString) RCT_EXPORT_VIEW_PROPERTY(titleColor, UIColor) +RCT_EXPORT_VIEW_PROPERTY(progressViewOffset, CGFloat) RCT_EXPORT_METHOD(setNativeRefreshing : (nonnull NSNumber *)viewTag toRefreshing : (BOOL)refreshing) {