SwiftUI Map keeps overriding appearance of ToolBar / NavigationBar

When adding a Map using the MapKit this changes the appearance of the TabBar appearance, specifically gives the toolbar a translucent background with a shadow; the default iOS style.

I have tried adding the following modifier to the Map

.toolbarBackground(Color("White"), for: .navigationBar)

But the navigation toolbar still has a shadow, and the TabBar has the default translucent background colour with shadow.

Root

init() {
    // this is not the same as manipulating the proxy directly
    let appearance = UINavigationBarAppearance()
    
    // this overrides everything you have set up earlier.
    appearance.configureWithTransparentBackground()
    
    appearance.shadowColor = .clear
    
    //In the following two lines you make sure that you apply the style for good
    UINavigationBar.appearance().scrollEdgeAppearance = appearance
    UINavigationBar.appearance().standardAppearance = appearance
    
    
    UITabBar.appearance().barTintColor = UIColor.white)
    UITabBar.appearance().backgroundColor = UIColor.white)
    UITabBar.appearance().shadowImage = UIImage()
    UITabBar.appearance().backgroundImage = UIImage()
    
    UINavigationBar.appearance().isTranslucent = false
    UIToolbar.appearance().backgroundColor = UIColor.white)
    UIToolbar.appearance().isTranslucent = false
    UIToolbar.appearance().setShadowImage(UIImage(), forToolbarPosition: .any)
}

struct MainView: View {
    var body: some View {
        TabView {
            ContentView()
                .tabItem {
                    Label("Menu", systemImage: "list.dash")
                }
        }
    }
}

The Tabbar has a solid white colour with no shadow, as well as navigation bars.

Content View

struct ContentView: View {
    var body: some View {
        NavigationStack() {
            NavigationLink(destination: MapView()) {
              Text("Hello, World!")
            }
        }
        .navigationTitle("Content")
    }
}

MapView

struct MapView: View {
    @State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 51.507222, longitude: -0.1275), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))

    var body: some View {
        Map(coordinateRegion: $region)
                    .mapControlVisibility(.hidden)
                    .allowsHitTesting(false)
                    .frame(maxWidth: .infinity)
                    .frame(height: 414)
                    .clipped()
    }
}

I have looked through the documentation but could not find anything. https://developer.apple.com/documentation/mapkit/map

I have the same issue. The following code works everywhere except views that have Map() in it. In those, everything seems to be reset to defaults…

let normalTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor(Theme.secondary)]
UITabBarItem.appearance().setTitleTextAttributes(normalTextAttributes, for: .normal)

let selectedTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor(Theme.primary)]
UITabBarItem.appearance().setTitleTextAttributes(normalTextAttributes, for: .selected)

let tabBarItemAppearance = UITabBarItemAppearance()
tabBarItemAppearance.normal.titleTextAttributes = normalTextAttributes
tabBarItemAppearance.normal.iconColor = UIColor(Theme.secondary)
tabBarItemAppearance.selected.titleTextAttributes = selectedTextAttributes
tabBarItemAppearance.selected.iconColor = UIColor(Theme.primary)

let tabBarAppearance = UITabBarAppearance()
tabBarAppearance.backgroundColor = UIColor(Theme.background)
tabBarAppearance.inlineLayoutAppearance = tabBarItemAppearance
tabBarAppearance.stackedLayoutAppearance = tabBarItemAppearance
tabBarAppearance.compactInlineLayoutAppearance = tabBarItemAppearance

UITabBar.appearance().isTranslucent = false
UITabBar.appearance().standardAppearance = tabBarAppearance
UITabBar.appearance().scrollEdgeAppearance = tabBarAppearance

Were you able to solve your issue?

Here's a complete example:

import SwiftUI
import MapKit

struct Theme {
  static let primary = Color.red
  static let secondary = Color.green
  static let background = Color.gray
}

@MainActor
struct ViewTabs: View {
  @State var tag: UInt8 = 1

  var body: some View {
    TabView(selection: $tag) {
      Map()
        .tag(0)
        .tabItem { Label("Map", systemImage: "map") }

      Text("hello")
        .tag(1)
        .tabItem { Label("Text", systemImage: "character") }

      Image(systemName: "dog")
        .tag(2)
        .tabItem { Label("Dog", systemImage: "dog") }
    }
    .onAppear(perform: ViewTabs.styleTabBar)
  }

  static func styleTabBar () {
    let normalTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor(Theme.secondary)]
    UITabBarItem.appearance().setTitleTextAttributes(normalTextAttributes, for: .normal)

    let selectedTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor(Theme.primary)]
    UITabBarItem.appearance().setTitleTextAttributes(normalTextAttributes, for: .selected)

    let tabBarItemAppearance = UITabBarItemAppearance()
    tabBarItemAppearance.normal.titleTextAttributes = normalTextAttributes
    tabBarItemAppearance.normal.iconColor = UIColor(Theme.secondary)
    tabBarItemAppearance.selected.titleTextAttributes = selectedTextAttributes
    tabBarItemAppearance.selected.iconColor = UIColor(Theme.primary)

    let tabBarAppearance = UITabBarAppearance()
    tabBarAppearance.backgroundColor = UIColor(Theme.background)
    tabBarAppearance.inlineLayoutAppearance = tabBarItemAppearance
    tabBarAppearance.stackedLayoutAppearance = tabBarItemAppearance
    tabBarAppearance.compactInlineLayoutAppearance = tabBarItemAppearance

    UITabBar.appearance().isTranslucent = false
    UITabBar.appearance().standardAppearance = tabBarAppearance
    UITabBar.appearance().scrollEdgeAppearance = tabBarAppearance
  }
}

#Preview {
  ViewTabs()
}

Replacing Map() with the following works as expected:

@MainActor
struct AppKitMap: UIViewRepresentable {
  let view = MKMapView()

  func makeUIView(context: Context) -> MKMapView {
    return view
  }
  
  func updateUIView(_ uiView: MKMapView, context: Context) {}
}

The issue seems to be the .toolbarBackground modifier, not the Map itself. I see the same problem when using a MKMapView, as long as I add that modifier.

However, as you correctly described, Map will underlap the navigation bar by default and thus requires the .toolbarBackground modifier to achieve the desired look.

Sadly I couldn't yet find a solution for customizing the back button appearance (which, to my knowledge, isn't possible without resorting to UIKit and appearance proxies) while using .toolbarBackground. Any pointers are much appreciated!

SwiftUI Map keeps overriding appearance of ToolBar / NavigationBar
 
 
Q