Skip to content

fix(ios): prevent SwiftUI filter content collapse after view recycling#57643

Open
lujjjh wants to merge 1 commit into
react:mainfrom
lujjjh:fix/ios-swiftui-filter-safe-area
Open

fix(ios): prevent SwiftUI filter content collapse after view recycling#57643
lujjjh wants to merge 1 commit into
react:mainfrom
lujjjh:fix/ios-swiftui-filter-safe-area

Conversation

@lujjjh

@lujjjh lujjjh commented Jul 23, 2026

Copy link
Copy Markdown

Summary:

Fixes #57642

SwiftUI-based filters can render with zero-height content after Fabric recycles a view, even though the outer React Native view and hosting view retain their expected dimensions.

This disables safe-area handling for the internal UIHostingController, since its layout is already controlled by React Native:

  • Uses safeAreaRegions = [] on iOS 16.4 and later.
  • Applies the equivalent safeAreaInsets override only to the hosting view instance on earlier supported versions.

Changelog:

[IOS] [FIXED] - Prevent SwiftUI-based filter content from collapsing after view recycling.

Test Plan:

  • Reproduced the issue on the two iOS versions tested: iOS 16.0 and iOS 26.5.
  • Verified that the filtered content remains visible after applying the fix on both versions.

@meta-cla

meta-cla Bot commented Jul 23, 2026

Copy link
Copy Markdown

Hi @lujjjh!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

@meta-cla

meta-cla Bot commented Jul 23, 2026

Copy link
Copy Markdown

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jul 23, 2026
@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Jul 23, 2026
@cipolleschi

Copy link
Copy Markdown
Contributor

I don't think this is the right fix. This is doing a lot of shenaningan with the objc runtime to fix a recycle issue.
I think that the right fix is to make sure that the filter is actually recycled or that the props are forwarded and applied properly to the SwiftUI view.
@intergalacticspacehighway: do you mind having a look at the issue and suggest a better approach, given that you worked on the implementation?
@lujjjh it would be great if you can provide a reproducer in RNTester app (we have a playground.js file you can use) so we can verify the issue ourselves and look for a better fix.

@lujjjh

lujjjh commented Jul 24, 2026

Copy link
Copy Markdown
Author

@cipolleschi Thanks for the reply. I agree that the Objective-C runtime fallback deserves scrutiny. For context, the pre-iOS 16.4 fallback was adapted from an earlier Expo Modules Core implementation:

https://github.com/expo/expo/blob/b9503d06b8e130abfca1bc16f671dbefb41ad709/packages/expo-modules-core/ios/Core/Views/SwiftUI/SwiftUIHostingView.swift#L262-L304

Here is the RNTester Playground reproducer:

https://github.com/lujjjh/react-native-swiftui-filter-repro/tree/repro/ios-swiftui-filter-recycle

Detailed reproduction steps and the observed native view hierarchy are documented in #57642.

One important detail is that the views mounted before recycling do not have filters. The native RCTViewComponentView backing the filtered target is recycled, but its SwiftUI wrapper and UIHostingController are created only after the filter props are applied. The filter-rendering hierarchy itself is therefore not reused from a previously filtered view.

In the failing state, the outer RCTViewComponentView, _UIHostingView, and React Native child all retain their expected 28-point height, while only the internal SwiftUI hierarchy becomes zero-height. Disabling safe-area handling prevents the collapse on both tested versions, iOS 16.0 and iOS 26.5.

My rationale for this approach is that the SwiftUI hierarchy is introduced as an implementation detail of filter rendering, and a visual filter should be layout-transparent: applying it should not change the React Native child’s geometry or safe-area behavior. This does not rule out a missing layout or safe-area invalidation during recycling, but disabling safe-area handling still seems appropriate for a filter wrapper that should not affect layout.

@benslimanh

Copy link
Copy Markdown
Contributor

you should avoid the safe area hack and clear the swiftUI wrapper's state in prepareForRecycle.

@lujjjh
lujjjh force-pushed the fix/ios-swiftui-filter-safe-area branch from 9706ae6 to b8dd946 Compare July 24, 2026 16:48
@lujjjh

lujjjh commented Jul 24, 2026

Copy link
Copy Markdown
Author

@benslimanh Thanks for the suggestion. However, clearing the SwiftUI wrapper’s state in prepareForRecycle does not affect this reproducer. Before recycling, the native RCTViewComponentView represents a React Native view without a filter, so _swiftUIWrapper is still nil when prepareForRecycle runs—there is no SwiftUI renderer state to clear. The wrapper and UIHostingController are created only afterward, when the recycled component view receives the filtered target’s props.

My current findings indicate that the recycle-specific collapse is caused by incomplete view-controller containment. React Native inserts the hosting controller’s view directly without attaching the hosting controller to a parent view controller. I removed the safe-area workaround and replaced it with standard containment using addChild and didMove(toParent:). This fixes the original collapse when the filtered view is positioned away from the safe-area edges.

However, testing at the bottom of the screen shows that containment does not eliminate the need to handle safe areas. With identical filtered and unfiltered 140×100 views, the filtered SwiftUI content is reduced to 140×66, while the unfiltered content remains 140×100. The missing 34 points correspond exactly to the bottom safe-area inset.

So containment fixes the recycling lifecycle issue, but safe-area suppression is still required to keep filters layout-transparent. The remaining question is how best to implement that behavior, particularly before the public safeAreaRegions API is available.

@intergalacticspacehighway

Copy link
Copy Markdown
Contributor

@lujjjh can you verify if passing below modifier ignores the safe area insets in original repro (without the safe area regions removal)?

UIViewWrapper(view: contentView)
      .ignoresSafeArea()

Issue with attaching controller is that we'll also need to avoid attaching controller if the parent VC is UINavigationController or UITabBarController - here

Currently i am afk, will try a more minimal repro in plain swiftui/uikit tomorrow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SwiftUI-based filter content collapses after Fabric view recycling on iOS

4 participants