From b8dd946c410888125cd47e1a87639ccf5dfc9c24 Mon Sep 17 00:00:00 2001 From: Jiahao Lu Date: Thu, 23 Jul 2026 15:19:50 +0800 Subject: [PATCH] fix(ios): prevent SwiftUI filter content collapse --- .../View/RCTViewComponentView.mm | 28 +++++++++++++++++-- .../RCTSwiftUI/RCTSwiftUIContainerView.swift | 27 ++++++++++++++++++ .../RCTSwiftUIContainerViewWrapper.h | 2 ++ .../RCTSwiftUIContainerViewWrapper.m | 10 +++++++ 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm index fc4493544f34..bd5e93d5b597 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm @@ -23,6 +23,7 @@ #import #import #import +#import #import #import #import @@ -726,6 +727,23 @@ - (void)prepareForRecycle _layoutMetrics = {}; } +- (void)updateSwiftUIWrapperAttachment +{ + if (_swiftUIWrapper == nullptr) { + return; + } + + if (self.window == nil) { + [_swiftUIWrapper detachFromParentViewController]; + return; + } + + UIViewController *parentViewController = self.reactViewController; + if (parentViewController != nil) { + [_swiftUIWrapper attachToParentViewController:parentViewController inContainerView:self]; + } +} + - (void)setPropKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN:(NSSet *_Nullable)props { _propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN = props; @@ -927,7 +945,7 @@ - (UIView *)effectiveContentView self.layer.mask = nil; [_swiftUIWrapper updateContentView:swiftUIContentView]; [_swiftUIWrapper updateLayoutWithBounds:self.bounds]; - [self addSubview:_swiftUIWrapper.hostingView]; + [self updateSwiftUIWrapperAttachment]; [self transferVisualPropertiesFromView:self toView:swiftUIContentView]; } @@ -944,7 +962,7 @@ - (UIView *)effectiveContentView [self transferVisualPropertiesFromView:swiftUIContentView toView:self]; - [_swiftUIWrapper.hostingView removeFromSuperview]; + [_swiftUIWrapper detachFromParentViewController]; _swiftUIWrapper = nil; } } @@ -1401,6 +1419,12 @@ - (void)didMoveToSuperview } } +- (void)didMoveToWindow +{ + [super didMoveToWindow]; + [self updateSwiftUIWrapperAttachment]; +} + - (void)updateAccessibilityElements { if ([_accessibilityOrderNativeIDs count] == 0) { diff --git a/packages/react-native/ReactApple/RCTSwiftUI/RCTSwiftUIContainerView.swift b/packages/react-native/ReactApple/RCTSwiftUI/RCTSwiftUIContainerView.swift index 2ae423819ffb..7ca5798cc15e 100644 --- a/packages/react-native/ReactApple/RCTSwiftUI/RCTSwiftUIContainerView.swift +++ b/packages/react-native/ReactApple/RCTSwiftUI/RCTSwiftUIContainerView.swift @@ -29,6 +29,33 @@ import UIKit return hostingController?.view } + @objc public func attach(to parentViewController: UIViewController, in containerView: UIView) { + guard let hostingController else { + return + } + + if hostingController.parent === parentViewController && hostingController.view.superview === containerView { + return + } + + detachFromParentViewController() + parentViewController.addChild(hostingController) + containerView.addSubview(hostingController.view) + hostingController.didMove(toParent: parentViewController) + } + + @objc public func detachFromParentViewController() { + guard let hostingController else { + return + } + + if hostingController.parent != nil { + hostingController.willMove(toParent: nil) + } + hostingController.view.removeFromSuperview() + hostingController.removeFromParent() + } + @objc public func contentView() -> UIView? { return containerViewModel.contentView } diff --git a/packages/react-native/ReactApple/RCTSwiftUIWrapper/RCTSwiftUIContainerViewWrapper.h b/packages/react-native/ReactApple/RCTSwiftUIWrapper/RCTSwiftUIContainerViewWrapper.h index 6600f63efa3b..b36d73562bb5 100644 --- a/packages/react-native/ReactApple/RCTSwiftUIWrapper/RCTSwiftUIContainerViewWrapper.h +++ b/packages/react-native/ReactApple/RCTSwiftUIWrapper/RCTSwiftUIContainerViewWrapper.h @@ -21,6 +21,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)updateHueRotate:(NSNumber *)degrees; - (void)updateContentView:(UIView *)view; - (UIView *_Nullable)hostingView; +- (void)attachToParentViewController:(UIViewController *)parentViewController inContainerView:(UIView *)containerView; +- (void)detachFromParentViewController; - (void)resetStyles; - (void)updateLayoutWithBounds:(CGRect)bounds; diff --git a/packages/react-native/ReactApple/RCTSwiftUIWrapper/RCTSwiftUIContainerViewWrapper.m b/packages/react-native/ReactApple/RCTSwiftUIWrapper/RCTSwiftUIContainerViewWrapper.m index 2ba6ea546fae..8065f173d980 100644 --- a/packages/react-native/ReactApple/RCTSwiftUIWrapper/RCTSwiftUIContainerViewWrapper.m +++ b/packages/react-native/ReactApple/RCTSwiftUIWrapper/RCTSwiftUIContainerViewWrapper.m @@ -33,6 +33,16 @@ - (UIView *_Nullable)hostingView return [self.swiftContainerView hostingView]; } +- (void)attachToParentViewController:(UIViewController *)parentViewController inContainerView:(UIView *)containerView +{ + [self.swiftContainerView attachTo:parentViewController in:containerView]; +} + +- (void)detachFromParentViewController +{ + [self.swiftContainerView detachFromParentViewController]; +} + - (void)resetStyles { [self.swiftContainerView resetStyles];