From 6b235d16ab8819b2a8f9873d5a5534b3d76854bb Mon Sep 17 00:00:00 2001 From: Oleksii Bulenok Date: Wed, 15 Jul 2026 11:53:00 +0200 Subject: [PATCH] fix lightbox iOS issues, when opening an image with unknown aspect ratio --- patches/react-native@0.81.5.patch | 46 ++++++++++++++++++- patches/react-native@0.81.5.patch.md | 20 ++++++++ .../pager/ImageItem/ImageItem.ios.tsx | 18 +++++--- 3 files changed, 76 insertions(+), 8 deletions(-) diff --git a/patches/react-native@0.81.5.patch b/patches/react-native@0.81.5.patch index d7e0d10328..504a3f678d 100644 --- a/patches/react-native@0.81.5.patch +++ b/patches/react-native@0.81.5.patch @@ -1,3 +1,28 @@ +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 @@ -35,10 +60,27 @@ index 0d231bc8aa938da296eb3b981e8ac9595a43b87f..be0a10d9c4de1892fa00bcbf8d63d739 if (newConcreteProps.tintColor != oldConcreteProps.tintColor) { diff --git a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm -index 1494fd225aff1fa0429e917404d6b4ca5fc961c5..df643f5c844ad2e684de5161528eba17f4a188d0 100644 +index 1494fd225aff1fa0429e917404d6b4ca5fc961c5..682e41b141c38c830bbcd9f2ce0de07b18f13977 100644 --- a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm -@@ -1038,6 +1038,11 @@ - (void)_adjustForMaintainVisibleContentPosition +@@ -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); + } + +@@ -1038,6 +1046,11 @@ - (void)_adjustForMaintainVisibleContentPosition } } diff --git a/patches/react-native@0.81.5.patch.md b/patches/react-native@0.81.5.patch.md index adf2e4912d..e6ba33c310 100644 --- a/patches/react-native@0.81.5.patch.md +++ b/patches/react-native@0.81.5.patch.md @@ -25,6 +25,26 @@ props for the create-mutation path. Issue: https://github.com/facebook/react-native/issues/56343 +## RCTEnhancedScrollView.mm / RCTScrollViewComponentView.mm Patch - centerContent insets stale after content resize on New Arch + +**TODO: Remove after bumping React Native to 0.87+** (fixed upstream by facebook/react-native#56832, +commit d50c1b5207; first shipped in 0.87.0-rc.0). + +On Fabric, `centerContent` centers by computing `contentInset` in `centerContentIfNeeded`, but that +recompute only ran on `setFrame`/`didAddSubview`/`scrollViewDidZoom` - not when a state update assigns a +new `contentSize` in `updateState`. Any content that resizes after mount inside a `centerContent` +ScrollView (e.g. the lightbox image crop view getting its real aspect ratio from `onLoad` when the embed +has no aspectRatio metadata) keeps the old insets: content rests off-center and the excess inset creates +phantom scroll range, so the image can be dragged and parked off-center and the native scroll steals the +swipe-down-to-dismiss pan. The old architecture paired every `contentSize` update with re-centering in +`RCTScrollView.updateContentSizeIfNeeded`; Fabric dropped that link. + +Backport of the upstream fix: `setContentSize:`/`setCenterContent:` overrides on `RCTEnhancedScrollView` +that call `centerContentIfNeeded`, plus the `updateProps` guards so the `contentInset` prop does not +fight the computed centering inset. + +Issue: https://github.com/facebook/react-native/issues/55090 + ## RCTTextLayoutManager.mm Patch - Text overflows instead of wrapping on the last line Issue: https://github.com/react/react-native/issues/53450#issuecomment-3298157830 diff --git a/src/components/Lightbox/pager/ImageItem/ImageItem.ios.tsx b/src/components/Lightbox/pager/ImageItem/ImageItem.ios.tsx index 19373cbe1a..216ac176c0 100644 --- a/src/components/Lightbox/pager/ImageItem/ImageItem.ios.tsx +++ b/src/components/Lightbox/pager/ImageItem/ImageItem.ios.tsx @@ -170,8 +170,6 @@ const ImageItem = ({ width: screenSize.width, maxHeight: screenSize.height, alignSelf: 'center', - aspectRatio: imageAspect ?? 1 /* force onLoad */, - opacity: imageAspect === undefined ? 0 : 1, } }) @@ -180,11 +178,19 @@ const ImageItem = ({ return { transform: cropContentTransform, width: '100%', - aspectRatio: imageAspect ?? 1 /* force onLoad */, - opacity: imageAspect === undefined ? 0 : 1, } }) + /* + * When the aspect ratio is unknown until onLoad fires, these layout props + * change after mount. They must be applied via a React render rather than + * useAnimatedStyle + */ + const imageLayoutStyle = { + aspectRatio: imageAspect ?? 1 /* force onLoad */, + opacity: imageAspect === undefined ? 0 : 1, + } + const [showLoader, setShowLoader] = useState(false) const [hasLoaded, setHasLoaded] = useState(false) useAnimatedReaction( @@ -225,8 +231,8 @@ const ImageItem = ({ {showLoader && ( )} - - + +