In a SwiftUI app I have a MapView struct with the body containing a Map View with items based on an observed object (the data model). The MapFilterView in the sheet allows the user to toggle the displayed item categories in the data model. Map correctly reacts to a published change in the observed object by re-rendering itself. If Map displays MapMarkers for the items, no runtime warnings are shown when var body re-renders after a change of the observed object. However, as soon as I replace MapMarker with MapAnnotation and the body re-renders, I get "[SwiftUI] Publishing changes from within view updates is not allowed, this will cause undefined behavior." This warning is shown for each item that was visible in the Map before the re-render.
My goal is to have no runtime warnings and some sort of marker on the map, where a tap on the marker pushes to a detail view of the item. MapMarker does not seem to allow navigation. MapAnnotation can include a NavigationLink, but all variants of MapAnnotation that I have tried - with or without NavigationLink - generate the above runtime warning. Is it because of the trailing closure of the MapAnnotation? Is it because MapAnnotation has as Content a SwiftUI View, whereas MapMarker and MapPin are only structs without a View? Can anyone suggest a workaround with no runtime warnings and a working push navigation?
Code with MapMarker, no runtime warnings
import SwiftUI
import MapKit
struct ItemAnnotationView: View {
let mapItem: CatalogItem
var body: some View {
Image(systemName: "mappin.circle.fill")
.resizable()
.scaledToFit()
.foregroundColor(Color(categoryColours[mapItem.catalogType]!))
.frame(width: 25, height: 25)
}
}
struct MapView: View {
@EnvironmentObject var dataModel: DataModel
@State private var showingFilter = false
@State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 44.65, longitude: 4.42), latitudinalMeters: 75000, longitudinalMeters: 75000)
var locationManager: Void = CLLocationManager()
.requestWhenInUseAuthorization()
var body: some View {
let _ = Self._printChanges()
// REMOVE FOR FINAL VERSION
NavigationView {
ZStack (alignment: .bottomTrailing){
Map(coordinateRegion: $region, showsUserLocation: true, annotationItems: dataModel.shownItems)
{ mapitem in
MapMarker(coordinate: mapitem.mapLocation, tint: Color(categoryColours[mapitem.catalogType]!))
}
Button(action: {
showingFilter.toggle()
}){
Label("Categories", systemImage: "checkmark.circle")
}
.padding(6)
.background(Color.black)
.font(.footnote)
.foregroundColor(.white)
.clipShape(Capsule())
.offset(x: -10, y: -35)
}
.navigationTitle("Map")
WelcomeView(viewType: .map)
}
.sheet(isPresented: $showingFilter) {
MapFilterView(showMapFilterView: $showingFilter)
}
}
}
As soon as I replace
MapMarker(coordinate: mapitem.mapLocation, tint: Color(categoryColours[mapitem.catalogType]!))
with
MapAnnotation(coordinate: mapitem.mapLocation) { ItemAnnotationView(mapItem: mapitem) }
the runtime warnings are generated for the already displayed annotations.
I originally had the code below with a working NavigationLink to a detail view, but because it also relies on MapAnnotation, it generates the same runtime warnings.
MapAnnotation(coordinate: mapitem.mapLocation) {
NavigationLink {
ItemDetail(item: mapitem)
} label: {
Image(systemName: "mappin.circle.fill")
.resizable()
.scaledToFit()
.foregroundColor(Color(categoryColours[mapitem.catalogType]!))
.frame(width: 25, height: 25)
}
}
Any ideas on how to circumvent the runtime warnings and have a working push navigation to a detail view?
The issue with MapAnnotation occurs with Xcode 14.0.1 / iOS 16.0, as with Xcode 14.1 beta 2 / iOS 16.1 beta (20B5045d).