Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot place SwiftUI view outside the SafeArea when embedded in UIHostingController

I have a simple SwiftUI view that contains 3 text elements:

struct ImageDescriptionView: View {
    var title: String?
    var imageDescription: String?
    var copyright: String?

    var body: some View {
        VStack(alignment: .leading) {
        
            if let title = title {
                Text(title)
                    .fontWeight(.bold)
                    .foregroundColor(.white)
                    .frame(maxWidth: .infinity, alignment: .leading)
            }
        
            if let imageDescription = imageDescription {
                Text(imageDescription)
                    .foregroundColor(.white)
                    .fontWeight(.medium)
                    .frame(maxWidth: .infinity, alignment: .leading)
            }
        
            if let copyright = copyright {
                Text(copyright)
                    .font(.body)
                    .foregroundColor(.white)
                    .frame(maxWidth: .infinity, alignment: .leading)
            }
        
        }
        .background(
            Color.blue
        )
    }
}

The SwiftUI View is embedded within a UIHostingController:

class ViewController: UIViewController {

    private var hostingController = UIHostingController(rootView: ImageDescriptionView(title: "25. November 2021", imageDescription: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.", copyright: "Bild © Unknown"))

    override func viewDidLoad() {
        super.viewDidLoad()
        setUpHC()
    }

    private func setUpHC() {
        hostingController.view.backgroundColor = .red
    
        view.addSubview(hostingController.view)
        hostingController.view.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            hostingController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            hostingController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            hostingController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
        addChild(hostingController)
        hostingController.didMove(toParent: self)
    }

}

The result looks like this:

enter image description here

The UIHostingController is always bigger than the view. Also, it will always make the SwiftUI view respect the safe area (which in my case, I do not want)

The look I want:

(please don't comment the usability of the home indicator, that's not the case here)

enter image description here

What's the problem with UIHostingController? I tried setting .edgesIgnoreSafeArea(.all) on all Views within ImageDescriptionView, did not help.

like image 391
WalterBeiter Avatar asked Sep 14 '25 16:09

WalterBeiter


2 Answers

iOS 16.4 Update

There's now an official API in iOS 16.4 for the previously private API method:

viewController.safeAreaRegions = SafeAreaRegions()

The SafeAreaRegions() initialiser returns an empty collection. Default value of .safeAreaRegions is .all.

Backwards compatible answer

If you want to be backwards compatible and support iOS 13+, there is a workaround:

let controller = UIHostingController(rootView: buttonView)
let window = UIApplication.shared.windows.first(where: \.isKeyWindow)
let inverseSafeAreaInset = window?.safeAreaInsets.bottom ?? 0
controller.additionalSafeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: -inverseSafeAreaInset, right: 0)

Note you can get the inverse offset from the parent view / VC in multiple ways. e.g. view.safeAreaInsets works once the view has been laid out, however the window approach works for all contexts.

like image 199
Pranav Kasetti Avatar answered Sep 16 '25 15:09

Pranav Kasetti


On the UIHostingControllers property try the following

hostingController._disableSafeArea = true

iOS 16.4+ 

hostingController.safeAreaRegions = .all

that should do the trick.

like image 36
YoelL Avatar answered Sep 16 '25 16:09

YoelL