From 3bab7f7540273a50501780b2f92076fbe41c8022 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 13 Nov 2024 16:34:24 +0000 Subject: [PATCH] Patch RCTScrollView to fix centerContent (#6298) --- patches/react-native+0.74.1.patch | 137 +++++++++++++++++++++++++++--- 1 file changed, 125 insertions(+), 12 deletions(-) diff --git a/patches/react-native+0.74.1.patch b/patches/react-native+0.74.1.patch index ea6161e2ff..d9f55aa308 100644 --- a/patches/react-native+0.74.1.patch +++ b/patches/react-native+0.74.1.patch @@ -9,7 +9,7 @@ index caa5540..c5d4e67 100644 - type != nil && [type length] > 0 ? type : @"application/octet-stream", + ![type isEqual:[NSNull null]] && [type length] > 0 ? type : @"application/octet-stream", [data base64EncodedStringWithOptions:0]]; - + resolve(text); diff --git a/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm b/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm index b0d71dc..41b9a0e 100644 @@ -18,7 +18,7 @@ index b0d71dc..41b9a0e 100644 @@ -377,10 +377,6 @@ - (void)textInputDidBeginEditing self.backedTextInputView.attributedText = [NSAttributedString new]; } - + - if (_selectTextOnFocus) { - [self.backedTextInputView selectAll:nil]; - } @@ -35,7 +35,7 @@ index b0d71dc..41b9a0e 100644 + [self.backedTextInputView selectAll:nil]; + } } - + - (void)reactBlur diff --git a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h index e9b330f..ec5f58c 100644 @@ -48,10 +48,10 @@ index e9b330f..ec5f58c 100644 +@property (nonatomic, copy) UIColor *customTintColor; + +- (void)forwarderBeginRefreshing; - + @end diff --git a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m -index b09e653..288e60c 100644 +index b09e653..d2b4e05 100644 --- a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m +++ b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m @@ -22,6 +22,7 @@ @implementation RCTRefreshControl { @@ -60,12 +60,12 @@ index b09e653..288e60c 100644 CGFloat _progressViewOffset; + UIColor *_customTintColor; } - + - (instancetype)init @@ -56,6 +57,12 @@ - (void)layoutSubviews _isInitialRender = false; } - + +- (void)didMoveToSuperview +{ + [super didMoveToSuperview]; @@ -78,7 +78,7 @@ index b09e653..288e60c 100644 @@ -203,4 +210,58 @@ - (void)refreshControlValueChanged } } - + +- (void)setCustomTintColor:(UIColor *)customTintColor +{ + _customTintColor = customTintColor; @@ -139,26 +139,139 @@ index 40aaf9c..1c60164 100644 --- a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControlManager.m +++ b/node_modules/react-native/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/node_modules/react-native/React/Views/ScrollView/RCTScrollView.m b/node_modules/react-native/React/Views/ScrollView/RCTScrollView.m +index 1aead51..39e6244 100644 +--- a/node_modules/react-native/React/Views/ScrollView/RCTScrollView.m ++++ b/node_modules/react-native/React/Views/ScrollView/RCTScrollView.m +@@ -159,31 +159,6 @@ - (BOOL)touchesShouldCancelInContentView:(__unused UIView *)view + return !shouldDisableScrollInteraction; + } + +-/* +- * Automatically centers the content such that if the content is smaller than the +- * ScrollView, we force it to be centered, but when you zoom or the content otherwise +- * becomes larger than the ScrollView, there is no padding around the content but it +- * can still fill the whole view. +- */ +-- (void)setContentOffset:(CGPoint)contentOffset +-{ +- UIView *contentView = [self contentView]; +- if (contentView && _centerContent && !CGSizeEqualToSize(contentView.frame.size, CGSizeZero)) { +- CGSize subviewSize = contentView.frame.size; +- CGSize scrollViewSize = self.bounds.size; +- if (subviewSize.width <= scrollViewSize.width) { +- contentOffset.x = -(scrollViewSize.width - subviewSize.width) / 2.0; +- } +- if (subviewSize.height <= scrollViewSize.height) { +- contentOffset.y = -(scrollViewSize.height - subviewSize.height) / 2.0; +- } +- } +- +- super.contentOffset = CGPointMake( +- RCTSanitizeNaNValue(contentOffset.x, @"scrollView.contentOffset.x"), +- RCTSanitizeNaNValue(contentOffset.y, @"scrollView.contentOffset.y")); +-} +- + - (void)setFrame:(CGRect)frame + { + // Preserving and revalidating `contentOffset`. +@@ -427,6 +402,11 @@ - (void)setRemoveClippedSubviews:(__unused BOOL)removeClippedSubviews + // Does nothing + } + ++- (void)setFrame:(CGRect)frame { ++ [super setFrame:frame]; ++ [self centerContentIfNeeded]; ++} ++ + - (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex + { + [super insertReactSubview:view atIndex:atIndex]; +@@ -444,6 +424,8 @@ - (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex + RCTApplyTransformationAccordingLayoutDirection(_contentView, self.reactLayoutDirection); + [_scrollView addSubview:view]; + } ++ ++ [self centerContentIfNeeded]; + } + + - (void)removeReactSubview:(UIView *)subview +@@ -652,9 +634,46 @@ -(void)delegateMethod : (UIScrollView *)scrollView \ + } + + RCT_SCROLL_EVENT_HANDLER(scrollViewWillBeginDecelerating, onMomentumScrollBegin) +-RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, onScroll) + RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop) + ++-(void)scrollViewDidZoom : (UIScrollView *)scrollView ++{ ++ [self centerContentIfNeeded]; ++ ++ RCT_SEND_SCROLL_EVENT(onScroll, nil); ++ RCT_FORWARD_SCROLL_EVENT(scrollViewDidZoom : scrollView); ++} ++ ++/* ++ * Automatically centers the content such that if the content is smaller than the ++ * ScrollView, we force it to be centered, but when you zoom or the content otherwise ++ * becomes larger than the ScrollView, there is no padding around the content but it ++ * can still fill the whole view. ++ * ++ * PATCHED: This deviates from the original React Native implementation to fix two issues: ++ * ++ * - The scroll view was swallowing any taps immediately after pinching ++ * - The scroll view was jittering when crossing the full screen threshold while pinching ++ * ++ * This implementation is based on https://petersteinberger.com/blog/2013/how-to-center-uiscrollview/. ++ */ ++-(void)centerContentIfNeeded ++{ ++ if (_scrollView.centerContent && ++ !CGSizeEqualToSize(self.contentSize, CGSizeZero) && ++ !CGSizeEqualToSize(self.bounds.size, CGSizeZero) ++ ) { ++ CGFloat top = 0, left = 0; ++ if (self.contentSize.width < self.bounds.size.width) { ++ left = (self.bounds.size.width - self.contentSize.width) * 0.5f; ++ } ++ if (self.contentSize.height < self.bounds.size.height) { ++ top = (self.bounds.size.height - self.contentSize.height) * 0.5f; ++ } ++ _scrollView.contentInset = UIEdgeInsetsMake(top, left, top, left); ++ } ++} ++ + - (void)addScrollListener:(NSObject *)scrollListener + { + [_scrollListeners addObject:scrollListener]; +@@ -913,6 +932,7 @@ - (void)updateContentSizeIfNeeded + CGSize contentSize = self.contentSize; + if (!CGSizeEqualToSize(_scrollView.contentSize, contentSize)) { + _scrollView.contentSize = contentSize; ++ [self centerContentIfNeeded]; + } + } + diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java index 5f5e1ab..aac00b6 100644 --- a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java +++ b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java @@ -99,8 +99,9 @@ public class JavaTimerManager { } - + // If the JS thread is busy for multiple frames we cancel any other pending runnable. - if (mCurrentIdleCallbackRunnable != null) { - mCurrentIdleCallbackRunnable.cancel(); @@ -166,5 +279,5 @@ index 5f5e1ab..aac00b6 100644 + if (currentRunnable != null) { + currentRunnable.cancel(); } - + mCurrentIdleCallbackRunnable = new IdleCallbackRunnable(frameTimeNanos);