lhunath
lhunath

Reputation: 125456

Importing SwiftUI safeAreaInset into UIKit

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

Upvotes: 5

Views: 582

Answers (1)

Mojtaba Hosseini
Mojtaba Hosseini

Reputation: 119666

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.

Upvotes: 1

Related Questions