Skip to content

Commit fea820f

Browse files
fix(iOS) - Prevent SwiftUI based filter from insetting content by the safe area (#58349)
Summary: When a `filter` style is passed to a `View`, it can shift the `View` by safe area insets if it overlaps a safe area edge. Filter style should not affect layout. Also fixes - #57642 Unset [safeAreaRegions](https://developer.apple.com/documentation/swiftui/uihostingcontroller/safearearegions) on the hosting controller to opt out of implicit safe area insets. Below iOS 16.4 `safeAreaRegions` does not exist so it requires Obj-C swizzling hack as noted here - #57643. Hence, proper fix requires gating the filter feature to iOS 16.4. We can mention it in the documentation on release. ## Changelog: [IOS] [FIXED] - Fix views with a filter having their content offset by the safe area insets <!-- Help reviewers and the release process by writing your own changelog entry. Pick one each for the category and type tags: For more details, see: https://reactnative.dev/contributing/changelogs-in-pull-requests Pull Request resolved: #58349 Test Plan: - Set `releaseLevel` to canary. - Render `<View style={{width: 100, height: 100, backgroundColor:"red", filter:[{blur: 10}]}} />` in RNTester, make sure it renders on top of the screen. - View renders as expected on first mount. - Change height or width, it will shift the view by safe area inset. It requires layout update because RCTMountingManager calls `finalizeUpdates` before calling `mountChildComponentView`, so on first mount the hosting view has no window and its safe area insets are zero. | Before | After | | --- | --- | | <img width="200" alt="Filtered content pushed down by the top safe area inset" src="https://github.com/user-attachments/assets/49905f7b-0a82-4958-91d2-e89c852e5a93" /> | <img width="200" alt="Filtered content aligned with the view bounds" src="https://github.com/user-attachments/assets/85848179-43fc-42f4-9271-1093413ad42d" /> | | Content is offset down and shrunk by the top inset | Content fills the view | cc - jorge-cab Reviewed By: christophpurrer Differential Revision: D119153463 Pulled By: cipolleschi fbshipit-source-id: edabd933b4928dd023109eba5d09b6447256615f
1 parent 5dcbbae commit fea820f

2 files changed

Lines changed: 12 additions & 1 deletion

File tree

packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1746,7 +1746,13 @@ - (NSString *)componentViewName_DO_NOT_USE_THIS_IS_BROKEN
17461746

17471747
- (BOOL)styleNeedsSwiftUIContainer
17481748
{
1749-
if (!_props->filter.empty()) {
1749+
if (_props->filter.empty()) {
1750+
return NO;
1751+
}
1752+
1753+
// A filter must not affect layout, but UIHostingController insets its content by the safe area.
1754+
// To disable the insets we use `safeAreaRegions` which is only available in iOS 16.4 and tvOS 16.4.
1755+
if (@available(iOS 16.4, tvOS 16.4, *)) {
17501756
for (const auto &primitive : _props->filter) {
17511757
if (primitive.type == FilterType::Blur || primitive.type == FilterType::Grayscale ||
17521758
primitive.type == FilterType::DropShadow || primitive.type == FilterType::Saturate ||
@@ -1755,6 +1761,7 @@ - (BOOL)styleNeedsSwiftUIContainer
17551761
}
17561762
}
17571763
}
1764+
17581765
return NO;
17591766
}
17601767

packages/react-native/ReactApple/RCTSwiftUI/RCTSwiftUIContainerView.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ import UIKit
1515
@objc public override init() {
1616
super.init()
1717
hostingController = UIHostingController(rootView: SwiftUIContainerView(viewModel: containerViewModel))
18+
if #available(iOS 16.4, tvOS 16.4, *) {
19+
// Disable implicit safe area insets or else the view is shifted by the safe area insets
20+
hostingController?.safeAreaRegions = []
21+
}
1822
guard let view = hostingController?.view else {
1923
return
2024
}

0 commit comments

Comments
 (0)