fix scrollToTop not working on certain tabs

This commit is contained in:
Oleksii Bulenok
2026-07-14 14:07:25 +02:00
parent e10bb60ae7
commit 67beb20c92
2 changed files with 93 additions and 0 deletions
@@ -1,3 +1,84 @@
diff --git a/ios/Fabric/RNCPagerViewComponentView.mm b/ios/Fabric/RNCPagerViewComponentView.mm
index 652a5c123100e7010011f07649517aa9e0cbc554..9e038c353298f33f4f0fdb9d01cec38841b7a5c9 100644
--- a/ios/Fabric/RNCPagerViewComponentView.mm
+++ b/ios/Fabric/RNCPagerViewComponentView.mm
@@ -90,6 +90,62 @@ - (void)willMoveToSuperview:(UIView *)newSuperview {
}
}
+/*
+ * UIKit resolves several behaviors (status-bar-tap scroll-to-top, safe area
+ * propagation, appearance callbacks) by walking parentViewController from a
+ * view's nearest view controller up to the window's root view controller.
+ * The Paper implementation embeds the UIPageViewController into that chain
+ * via reactAddControllerToClosestParent:, but this Fabric implementation
+ * leaves it orphaned (parentViewController == nil), which among other things
+ * makes UIKit ignore every scroll view rendered inside the pager when
+ * handling the status bar scroll-to-top tap. Attach the page view controller
+ * to the nearest ancestor view controller to restore parity with Paper.
+ */
+- (void)attachNativePageViewControllerToNearestParent {
+ if (_nativePageViewController == nil ||
+ _nativePageViewController.parentViewController != nil) {
+ return;
+ }
+ UIResponder *responder = self.nextResponder;
+ while (responder != nil && ![responder isKindOfClass:[UIViewController class]]) {
+ responder = responder.nextResponder;
+ }
+ UIViewController *parent = (UIViewController *)responder;
+ if (parent == nil) {
+ return;
+ }
+ [parent addChildViewController:_nativePageViewController];
+ [_nativePageViewController didMoveToParentViewController:parent];
+}
+
+- (void)detachNativePageViewControllerFromParent {
+ if (_nativePageViewController.parentViewController == nil) {
+ return;
+ }
+ [_nativePageViewController willMoveToParentViewController:nil];
+ [_nativePageViewController removeFromParentViewController];
+}
+
+- (void)didMoveToWindow {
+ [super didMoveToWindow];
+ if (self.window != nil) {
+ [self attachNativePageViewControllerToNearestParent];
+ }
+}
+
+- (void)layoutSubviews {
+ [super layoutSubviews];
+ /*
+ * On the first didMoveToWindow the ancestor view controller may not be
+ * wired up yet (see callstack/react-native-pager-view#1089 for the same
+ * timing issue in v8), so retry here; the attach is a cheap no-op once
+ * the controller has a parent.
+ */
+ if (self.window != nil) {
+ [self attachNativePageViewControllerToNearestParent];
+ }
+}
+
#pragma mark - React API
@@ -126,6 +182,13 @@ -(void)updateLayoutMetrics:(const facebook::react::LayoutMetrics &)layoutMetrics
-(void)prepareForRecycle {
[super prepareForRecycle];
+ /*
+ * Undo the child view controller relationship added in
+ * attachNativePageViewControllerToNearestParent, otherwise the parent
+ * view controller keeps the page view controller (and its subtree) alive
+ * after unmount.
+ */
+ [self detachNativePageViewControllerFromParent];
_nativePageViewController = nil;
_currentIndex = -1;
}
diff --git a/ios/RNCPagerView.m b/ios/RNCPagerView.m
index adfc7c6f2224b898a02319d352bb4fe11a18fd7e..939bb801c5b0ca6f93b77cb0507c19d137e08e77 100644
--- a/ios/RNCPagerView.m