I was curious about what is the best practice when you need to have a bunch of custom annotations on Map in SwiftUI. This is my first real IOS project, so I'm a little rough around the edges. Currently, I have 2400 annotations, they are buttons with custom images and eventually, when the user clicks on them they will pop up information about the art piece.
What I did so far was put all the Data of each pin in a CoreData with the proper relationships to the entity it represents and an image String to call in my asset folder directly from the MapAnnotation content.
I have run it on my own iPhone 8. Fps drops significantly. I'm not sure if it's because the amount of annotations is very big, but that will not go down. Here's an example of my MapView()
FYI, I fetch the pins onAppear() and on the buttons will be a filter also. The code is far from done, just trying to fix one issue before going too far to come back.
Thanks for any input. Have a great day
import SwiftUI import MapKit import CoreData
struct MapView: View {
@Environment(\.managedObjectContext) private var moc
@State var pins: [Pin] = []
@State var userTrackingMode: MapUserTrackingMode = .follow
// Location for Montreal downtown
@State var region = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 45.50240, longitude: -73.57067),
span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
var body: some View {
ZStack{
Map(coordinateRegion: $region,
interactionModes: MapInteractionModes.all,
showsUserLocation: true,
userTrackingMode: $userTrackingMode,
annotationItems: pins)
{ pin in
MapAnnotation(coordinate: CLLocationCoordinate2D(
latitude: pin.location?.latitude ?? 0,
longitude: pin.location?.longitude ?? 0))
{
Button(action: {
}){
if pin.isArtwork {
Image("\(pin.imageDefault!)")
.resizable()
.scaledToFit()
.frame(width: 10)
} else if pin.isPlace {
Image("\(pin.imageDefault ?? "place_pin")")
.resizable()
.scaledToFit()
.frame(width: 10)
}
}
}
}
.accentColor(Color.blue)
HStack(alignment: .bottom){
Spacer()
VStack(alignment: .trailing){
Spacer()
Button(action: {userTrackingMode = .follow}) {
Image("user_location")
.resizable()
.scaledToFit()
.frame(width: 40.0)
}
.padding()
.padding(.bottom, -20)
.cornerRadius(10)
.shadow(radius: /*@START_MENU_TOKEN@*/10/*@END_MENU_TOKEN@*/)
VStack{
Button(action: {
pins = fetchPins(predicate: "isArtwork == false")
}) {
Image("map_filter")
.resizable()
.scaledToFit()
.frame(width: 40.0)
}
.cornerRadius(10)
.shadow(radius: /*@START_MENU_TOKEN@*/10/*@END_MENU_TOKEN@*/)
.padding()
.onAppear {
pins = fetchPins(predicate: nil)
}
}
}
.padding(.bottom, 50)
}
}
}
}
extension MapView {
func fetchPins(predicate: String?) -> [Pin]{
do {
let request = Pin.fetchRequest() as NSFetchRequest<Pin>
if predicate != nil && predicate!.count > 0 {
let predicate = NSPredicate(format: predicate!)
request.predicate = predicate
}
return try moc.fetch(request)
} catch {
fatalError("Error fetching pin + predicate")
}
}
}