NavigationView embedded in a UIHostingController has additional safe area insets

Viewed 1564

Anyone know how to deal with this? Seems as though when you have a UIHostingController with a NavigationView the following happens:

enter image description here

Notice the big grey tab bar safe area.

This is primarily a UIKit application. Were replacing on of our tabs in the tab bar with A swiftUI view.

here is my code:

var body: some View {
    NavigationView {
        ZStack {
            MapKitMapView(
                mapView: mapView,
                annotations: $annotations,
                polylines: $polylines,
                centerCoordinate: $centerCoordinate,
                newMapRegion: $newMapRegion,
                userTrackingMode: $userTrackingMode
            )
            .edgesIgnoringSafeArea(.all)
            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
            
            ButtonLayer(mapView: mapView, userTrackingMode: $userTrackingMode, showSheet: $showSheet)
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .padding(.margin)
        }
        .edgesIgnoringSafeArea(.all)
        .navigationBarHidden(true)
        .sheet(isPresented: $showSheet) {
            SettingsView()
        }
    }
}

pardon the censorship. I want the app to remain anonymous.

But essentially my UITabBar is built as follows:

  • there is a global UINavigationViewController
  • the first and only item in the UINavigationController is this UITabBar
  • The UITabBar tabs are all created programatically.
  • The tab in question is just a UIHostingController with the code you see above as the rootView.

I have tried manually setting additional safe area insets to the UIHostingController to make it expand outside of the safe area.

If I remove the NavigationView everything looks and functions as intended.

2 Answers

I found a workaround for this issue. However, I would like to use a pure swiftUI solution rather than the below workaround. So I am leaving this open to see if someone else has some other insight.

Upon inspecting the view hierarchy debugger, I noticed the following:

The View is wrapped in a NotifyingMultiColumnSplitViewController

The navigation view style is SplitView... which is an iPad style. I am not actually setting my style. So maybe a bug is causing it to default to that.

I tried changing the NavigationView styling to StackNavigationViewStyle().

While this didn't fix the problem, It did cleanup the view hierarchy and wrap my UIHostingViewController in a proper UINavigationController.

It also changed the bottom grey "safe area" to white.

What this tells me, is that NavigationView just wraps your view in a standard UINavigationController when its being used in a UIHostingController. And since im interfacing with UIKit, maybe it makes more sense to set the root of my tab bar to a UINavigationController rather than a UIHostingController with a NavigationView inside of it.

So the tab that was experiencing the issue is now being populated with:

UINavigationController(rootViewController: UIHostingController(rootView: MyView()))

and my view code is just:

ZStack {
        MapKitMapView(
            mapView: mapView,
            annotations: $annotations,
            polylines: $polylines,
            centerCoordinate: $centerCoordinate,
            newMapRegion: $newMapRegion,
            userTrackingMode: $userTrackingMode
        )
        .edgesIgnoringSafeArea(.all)
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
        
        ButtonLayer(mapView: mapView, userTrackingMode: $userTrackingMode, showSheet: $showSheet)
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .padding(.margin)
    }
    .edgesIgnoringSafeArea(.all)
    .navigationBarHidden(true)
    .sheet(isPresented: $showSheet) {
        SettingsView()
    }

This actually corrected the issue. And I am able to push views without any problems using NavigationLinks.

I guess the lesson to learn here is:

SwiftUI interfaces with UIKit better than I thought.

What I still don't know is: Why was NavigationView having this problem if it just wraps your view in a UINavigationController? I tried reproducing it in a clean project and had no luck. So I suspect something being done in our app to customize the navigation bar or tab bar is causing this issue.

After much digging I could not find a root cause. So this workaround will have to suffice for the time being.

This situation happens when you embed UIHostingController in UITabBar. To remove this gray line, just set the translucency of the UITabBar.

tabBar.isTranslucent = true
Related