4

safeAreaInset(edge:alignment:spacing:content:):

The modified view is inset by the height of content, from edge, with its safe area increased by the same amount.

It appears that SwiftUI's safeAreaInset is not imported into UIKit's safe area:

        ZStack {
            Color.black
            UIKitView()
        }
        .ignoresSafeArea(edges: .top)
        .safeAreaInset(edge: .top) {
            Text("TOP")
                .frame(minHeight: 200)
                .background(Color.cyan.opacity(0.5))
        }

In this example, both the black view and the UIKitView are laid out the same - they ignore both the safeAreaInset:top and the status bar.

The question is: how can the UIKitView learn about the safe area in play?

Consider the following:

struct UIKitView: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> some UIViewController { ViewController() }
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
    
    class ViewController: UIViewController {
        let bgView = UILabel()
        let safeView = UILabel()

        override func viewDidLoad() {
            bgView.backgroundColor = .red.withAlphaComponent(0.5)
            self.view.addSubview(bgView)
            safeView.backgroundColor = .green.withAlphaComponent(0.5)
            self.view.addSubview(safeView)
        }

        override func viewWillLayoutSubviews() {
            self.bgView.frame = self.view.bounds
            self.safeView.frame = self.view.safeAreaLayoutGuide.layoutFrame
        }
    }
}

We now see two layers generated by the UIKitView, one that represents the full frame of the view (red), and one that is limited to the known safe area (green).

What is clear now is that UIKit's safe area is constrained by the status bar, but NOT by the safeAreaInset:top. This is surprising – how can we learn about the full top safe area in play and correctly adjust the green view to accommodate the content placed manually above it?

the resulting layout

lhunath
  • 120,288
  • 16
  • 68
  • 77
  • Not really clear... can you post an image of how you are ***expecting*** the views to layout? – DonMag Oct 17 '22 at 17:12
  • @DonMag since the "TOP" view is in a `.safeAreaInset`, it should be creating an additional safe area inset that the `UIKitView` should be able to observe, consequently the green area is expected to stop short of the top view rather than continuing up to below the status bar. Notice that the green view observes the system safe area but not the safe area created by `safeAreaInset`. – lhunath Oct 18 '22 at 20:44

1 Answers1

1

It seems like you mixed up some concepts.

  1. ZStack only stacks views on top of each other and does nothing to do with the safeArea.

  2. You are performing environmental layout actions inside the prebuilt UIViewController functions with some static codes, like assigning the frame directly. So it will NOT update the UI as desired.

A better example explains itself:

Try the following to see what you are looking for:

struct ContentView: View {
    var body: some View {
        NavigationView {
            ZStack {
                Color.black
                UIKitView()
            }
            .ignoresSafeArea(edges: .top)
            .navigationTitle("Hello World")
        }
    }
}

DemoImage

By adding a NavigationView which respects the safe area, you can see that:

  1. the SwiftUI.Color.Black goes beyond the top of the SafeArea below the NavigationBar but NOT below the bottom of the SafeArea

  2. The safeView(the green one) is completely in the safeArea as you manually set the safeAreaLayoutGuide.layoutFrame on it, which is the portion of your view that is unobscured by bars and other content

  3. The bgView(the red one) is sized and placed exactly like the SwiftUI.Color.Black. Because it is handled by the SwiftUI layout system.

So

  • If you want to let the SwiftUI control the layout, go with the frame.
  • If you want to ignore the SwiftUI layout and access the safe area of the environment (which can bث the status bar or any other bars like the navigation bar or even physical objects like the notch or the dynamic island), go with the internal view's safeAreaLayoutGuide.

Hope it explains the difference.

Mojtaba Hosseini
  • 95,414
  • 31
  • 268
  • 278
  • Ignoring `top` is correctly applied. Take a look at the **red** view and **black** color. Both of them are continued all the way up to the top edge of the device below both the navbar and the status bar – Mojtaba Hosseini Oct 25 '22 at 12:39
  • The question is not about whether ignoring top is correctly applied. Rather it is about why the environment's safe area includes only the status bar + nav bar, but not the `Text("TOP")` `safeAreaInset`. You have removed an element of the original problem and solved for that. – lhunath Oct 25 '22 at 15:08
  • @lhunath did you ever figure this one out? I've encountered the same issue :( – aehlke Mar 16 '23 at 04:09
  • 1
    @aehlke unfortunately not – lhunath Mar 17 '23 at 19:49