When I use non-deprecated Map() functions, it breaks the custom navigation bar and displays the system's default navigation bar instead. I haven’t been able to find a solution to this issue anywhere.
import SwiftUI
struct CustomNavigationBarView: ViewModifier {
@Environment(\.presentationMode) var presentationMode
var showBackButton: Bool
init(showBackButton: Bool = false) {
self.showBackButton = showBackButton
let navBarAppearance = UINavigationBarAppearance()
navBarAppearance.titleTextAttributes = [.foregroundColor: UIColor(Color.white)]
UINavigationBar.appearance().standardAppearance = navBarAppearance
UINavigationBar.appearance().compactAppearance = navBarAppearance
UINavigationBar.appearance().scrollEdgeAppearance = navBarAppearance
navBarAppearance.configureWithTransparentBackground()
navBarAppearance.backgroundImage = makeLinearGradient(
size: .init(width: 1, height: 1),
colors: [.nav1, .nav2]
)
navBarAppearance.shadowImage = UIImage()
}
func body(content: Content) -> some View {
content
.navigationBarBackButtonHidden(true)
.toolbar {
if showBackButton {
ToolbarItem(placement: .navigationBarLeading) {
Button(action: {
presentationMode.wrappedValue.dismiss()
}) {
HStack {
Image(systemName: "chevron.left")
.foregroundColor(.white)
}
}
}
}
}
}
}
func makeLinearGradient(size: CGSize, colors: [UIColor]) -> UIImage {
let renderer = UIGraphicsImageRenderer(size: size)
let colors: [CGColor] = colors.map({ $0.cgColor })
let gradient = CGGradient(
colorsSpace: CGColorSpaceCreateDeviceRGB(),
colors: colors as CFArray,
locations: [0, 1]
)
return renderer.image { context in
if let gradient {
context.cgContext.drawLinearGradient(
gradient,
start: CGPoint(x: 0, y: 1),
end: CGPoint(x: size.width, y: size.height),
options: .init()
)
}
}
}
extension View {
func navigationBarModifier(showBackButton: Bool = false) -> some View {
self.modifier(CustomNavigationBarView(showBackButton: showBackButton))
.navigationBarTitleDisplayMode(.inline)
}
}
Using the map function in this way preserves the navigation bar, but I’d rather avoid it because it’s deprecated.
let annotations = [
City(name: "London", coordinate: CLLocationCoordinate2D(latitude: 41.015137, longitude: 28.979530))
]
@State private var region = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 41.015137, longitude: 28.979530),
span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
ZStack(alignment: .bottom) {
Map(coordinateRegion: $region, annotationItems: annotations) { annotation in
MapAnnotation(coordinate: annotation.coordinate) {
Image("map-loc")
.resizable()
.frame(width: 25, height: 33)
}
}
}
Using this method breaks the custom navigation bar, and the other modern functions also fail to work as expected.
let cameraPosition: MapCameraPosition = .region(.init(center: .init(latitude: 41.015137, longitude: 28.979530), latitudinalMeters: 1300, longitudinalMeters: 1300))
ZStack(alignment: .bottom) {
Map(initialPosition: cameraPosition) {
Annotation("map", coordinate: .appleHQ, anchor: .bottom) {
Image(systemName: "map-loc")
.resizable()
}
}
}
It might help to add .toolbarBackground(.automatic, for: .navigationBar)
to the ZStack
that contains the Map
.
Here is an adaption of your example, based on an assumption of how you are applying the view extension:
struct ContentView: View {
let cameraPosition: MapCameraPosition = .region(.init(center: .init(latitude: 41.015137, longitude: 28.979530), latitudinalMeters: 1300, longitudinalMeters: 1300))
var body: some View {
NavigationStack {
NavigationLink("Go to map") {
ZStack(alignment: .bottom) {
Map(initialPosition: cameraPosition) {}
}
.toolbarBackground(.automatic, for: .navigationBar) // 👈 added
}
.navigationBarModifier() // 👈 is this how you are using your extension?
}
}
}
When the color array [.nav1, .nav2]
is replaced by [.yellow, .orange]
, it looks like this: