diff --git a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm index c593d9ee2155a826352ebca34845aa5792b2eec3..3c26cd737f21116ff0aa48190e97e6c0649b5fac 100644 --- a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm +++ b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm @@ -101,6 +101,20 @@ - (void)setContentOffset:(CGPoint)contentOffset RCTSanitizeNaNValue(contentOffset.y, @"scrollView.contentOffset.y")); } +- (void)setCenterContent:(BOOL)centerContent +{ + if (_centerContent != centerContent) { + _centerContent = centerContent; + [self centerContentIfNeeded]; + } +} + +- (void)setContentSize:(CGSize)contentSize +{ + [super setContentSize:contentSize]; + [self centerContentIfNeeded]; +} + - (void)setFrame:(CGRect)frame { [super setFrame:frame]; diff --git a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTPullToRefreshViewComponentView.h b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTPullToRefreshViewComponentView.h index 914a2494a57923fbf185644b7e2bb8aca8848e56..0deac55f22350f5e8377d8963fb1c2434bf6abfd 100644 --- a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTPullToRefreshViewComponentView.h +++ b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTPullToRefreshViewComponentView.h @@ -19,6 +19,8 @@ NS_ASSUME_NONNULL_BEGIN */ @interface RCTPullToRefreshViewComponentView : RCTViewComponentView +- (void)beginRefreshingProgrammatically; + @end NS_ASSUME_NONNULL_END diff --git a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm index 1494fd225aff1fa0429e917404d6b4ca5fc961c5..d0cce700090245444f8ce51e517d5ceca09526f6 100644 --- a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm @@ -380,7 +380,15 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & MAP_SCROLL_VIEW_PROP(zoomScale); - if (oldScrollViewProps.contentInset != newScrollViewProps.contentInset) { + // When disabling centerContent, reset inset to prop value + // (enabling is handled automatically by the setCenterContent: setter) + if (oldScrollViewProps.centerContent && !newScrollViewProps.centerContent) { + _scrollView.contentInset = RCTUIEdgeInsetsFromEdgeInsets(newScrollViewProps.contentInset); + } + + // Only apply contentInset from props if centerContent is disabled + // When centerContent is enabled, the inset is calculated by centerContentIfNeeded + if (oldScrollViewProps.contentInset != newScrollViewProps.contentInset && !newScrollViewProps.centerContent) { _scrollView.contentInset = RCTUIEdgeInsetsFromEdgeInsets(newScrollViewProps.contentInset); } @@ -507,7 +515,7 @@ - (UIView *)betterHitTest:(CGPoint)point withEvent:(UIEvent *)event } } - return isPointInside ? self : nil; + return isPointInside ? _scrollView : nil; } /* @@ -1133,6 +1133,11 @@ - (RCTVirtualViewContainerState *)virtualViewContainerState return _virtualViewContainerState; } ++ (BOOL)shouldBeRecycled +{ + return NO; +} + @end Class RCTScrollViewCls(void) diff --git a/React/Views/RefreshControl/RCTRefreshControl.h b/React/Views/RefreshControl/RCTRefreshControl.h index e9b330fa7c29c42653a3b0191d0f8a1b13b2d3de..5fbb2e05cadfc06fd7a18bf52b81bc399e92f3ca 100644 --- a/React/Views/RefreshControl/RCTRefreshControl.h +++ b/React/Views/RefreshControl/RCTRefreshControl.h @@ -15,5 +15,6 @@ @property (nonatomic, copy) NSString *title; @property (nonatomic, copy) RCTDirectEventBlock onRefresh; @property (nonatomic, weak) UIScrollView *scrollView; +@property (nonatomic, copy) UIColor *customTintColor; @end diff --git a/React/Views/RefreshControl/RCTRefreshControl.m b/React/Views/RefreshControl/RCTRefreshControl.m index 53bfd04703502d5b8e932c47a528bb03cd79d330..e2e0c9f4e5d1a3a3b178a7ec69aa63e3039b6dec 100644 --- a/React/Views/RefreshControl/RCTRefreshControl.m +++ b/React/Views/RefreshControl/RCTRefreshControl.m @@ -23,6 +23,7 @@ @implementation RCTRefreshControl { UIColor *_titleColor; CGFloat _progressViewOffset; BOOL _hasMovedToWindow; + UIColor *_customTintColor; } - (instancetype)init @@ -58,6 +59,12 @@ - (void)layoutSubviews _isInitialRender = false; } +- (void)didMoveToSuperview +{ + [super didMoveToSuperview]; + [self setTintColor:_customTintColor]; +} + - (void)didMoveToWindow { [super didMoveToWindow]; @@ -221,4 +228,16 @@ - (void)refreshControlValueChanged } } +// Fix for https://github.com/facebook/react-native/issues/43388 +// A bug in iOS 17.4 causes the haptic to not play when refreshing if the tintColor +// is set before the refresh control gets added to the scrollview. We'll call this +// function whenever the superview changes. We'll also call it if the value of customTintColor +// changes. +- (void)setTintColor:(UIColor *)tintColor +{ + if ([self.superview isKindOfClass:[UIScrollView class]] && self.tintColor != tintColor) { + [super setTintColor:tintColor]; + } +} + @end diff --git a/React/Views/RefreshControl/RCTRefreshControlManager.m b/React/Views/RefreshControl/RCTRefreshControlManager.m index 40aaf9c51ebda9fedb1d1db2e9aacec84b4c39c8..1c60164b69762997b3369b46609a07768a06bad3 100644 --- a/React/Views/RefreshControl/RCTRefreshControlManager.m +++ b/React/Views/RefreshControl/RCTRefreshControlManager.m @@ -22,11 +22,12 @@ - (UIView *)view RCT_EXPORT_VIEW_PROPERTY(onRefresh, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(refreshing, BOOL) -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_REMAP_VIEW_PROPERTY(tintColor, customTintColor, UIColor) + RCT_EXPORT_METHOD(setNativeRefreshing : (nonnull NSNumber *)viewTag toRefreshing : (BOOL)refreshing) { [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.kt b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.kt index 89b666dcf0258df0702c812600b685463128294c..2b1c3971f0c31a0d7a592b90170e4cc53a8a69dd 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.kt +++ b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.kt @@ -431,6 +431,13 @@ public open class ReactViewGroup public constructor(context: Context?) : inSubviewClippingLoop = true var clippedSoFar = 0 for (i in 0..