Home > Mobile >  SwiftUI get what Object's annotation was clicked on map. Core Data
SwiftUI get what Object's annotation was clicked on map. Core Data

Time:01-04

i have been trying to make so that when you press an annotation on the map that has been created by Core Data's object to show the LocationPreviewView(fish: fish). So how can i get the selected item and pass it to the LocationPreviewView() ?

I have already tried to do it by UIViewRepresentable way and already posted about it, but without success.

import SwiftUI
import MapKit
import CoreData
import CoreLocationUI
import StoreKit


struct MapMainView: View {
//???
    @State private var selectedFish: Fish?
    
    @Environment(\.managedObjectContext) private var moc
    @ObservedObject var locationManager = LocationManager.shared
    
    @State private var region = MKCoordinateRegion(center: LocationManager.currentLocation, span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1))
    
    
    var body: some View {
        
        ZStack {
            Map(coordinateRegion: $region,
                interactionModes: .all,
                showsUserLocation: true,
                annotationItems: annotations) {
                MapMarker(coordinate: $0.coordinate)
            }
                .ignoresSafeArea()
            VStack(spacing: 0) {
                
                
                ZStack {
                    ForEach(fetchFish()) { fish in
// Here pass the selected fish?????????
                        LocationPreviewView(fish: fish)
                            .shadow(color: Color.black.opacity(0.3), radius: 20)
                            .padding()
                            .transition(.asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .leading)))
                    }
                }
            }
            
        }
    }
    var annotations: [FishAnnotation] {
        let fish = fetchFish()
        return fish.map { FishAnnotation(name: $0.title ?? "No data found", coordinate: CLLocationCoordinate2D(latitude: $0.lat, longitude: $0.long)) }
    }
    
    func fetchFish() -> [Fish] {
        let request: NSFetchRequest<Fish> = Fish.fetchRequest()
        do {
            return try moc.fetch(request)
        } catch {
            print("Error fetching fish entities: \(error)")
            return []
        }
    }
}

struct MapMainView_Previews: PreviewProvider {
    
    @State static var mapView: MKMapView = MKMapView()
    
    static var previews: some View {
        MapMainView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
    }
}




CodePudding user response:

As i don't know your code i used a dummy entity

struct Fish {
    let id: UUID
    let title: String
    let lat: Double
    let long: Double    
}

and changed the FishAnnotation to:

struct FishAnnotation: Identifiable {
    let id = UUID()
    let fish: Fish
    
    var title: String { fish.title }
    var coordinate: CLLocationCoordinate2D { CLLocationCoordinate2D(latitude: fish.lat, longitude: fish.long) }
    
    init(fish: Fish) {
        self.fish = fish
    }
}

As the MapMarker doesn't conform to View i created a custom FishAnnotationView:

struct FishAnnotationView: View {
    let title: String

    var body: some View {
        VStack(spacing: 0) {
            Text(title)
                .font(.callout)
                .padding(5)
                .background(Color(.white))
                .cornerRadius(10)
            
            Image(systemName: "mappin.circle.fill")
                .font(.title)
                .foregroundColor(.red)
            
            Image(systemName: "arrowtriangle.down.fill")
                .font(.caption)
                .foregroundColor(.red)
                .offset(x: 0, y: -5)
        }
    }
}

If you want to show LocationPreviewView on the Map when the user select an annotation, you could do sth like:

struct MapMainView: View {
    @ObservedObject var locationManager = LocationManager.shared
    
    @Environment(\.managedObjectContext) private var moc
    @State private var region = MKCoordinateRegion(center: LocationManager.currentLocation, span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1))
    @State private var selectedFish: Fish?
    
    var body: some View {
        Map(coordinateRegion: $region,
            interactionModes: .all,
            showsUserLocation: true,
            annotationItems: annotations) { annotation in
            MapAnnotation(coordinate: annotation.coordinate) {
                FishAnnotationView(title: annotation.title)
                    .onTapGesture {
                        selectedFish = annotation.fish
                    }
            }
            
            if let selectedFish = selectedFish {
                VStack(spacing: 0) {
                    LocationPreviewView(fish: selectedFish)
                        .onTapGesture {
                            self.selectedFish = nil
                        }
                }
            }
        }
        .ignoresSafeArea()
    }
    
    var annotations: [FishAnnotation] {
        let fish = fetchFish()
        return fish.map { FishAnnotation(fish: $0) }
    }
    
    func fetchFish() -> [Fish] {
        let request: NSFetchRequest<Fish> = Fish.fetchRequest()
        do {
            return try moc.fetch(request)
        } catch {
            print("Error fetching fish entities: \(error)")
            return []
        }
    }
}

If you want to navigate to another view, you could do sth like:

struct MapMainView: View {
    @ObservedObject var locationManager = LocationManager.shared
    
    @Environment(\.managedObjectContext) private var moc
    @State private var region = MKCoordinateRegion(center: LocationManager.currentLocation, span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1))
    
    var body: some View {
        Map(coordinateRegion: $region,
            interactionModes: .all,
            showsUserLocation: true,
            annotationItems: annotations) { annotation in
            MapAnnotation(coordinate: annotation.coordinate) {
                NavigationLink {
                    LocationPreviewView(fish: annotation.fish)
                } label: {
                    FishAnnotationView(title: annotation.title)
                }
            }
        }
        .ignoresSafeArea()
    }
    
    var annotations: [FishAnnotation] {
        let fish = fetchFish()
        return fish.map { FishAnnotation(fish: $0) }
    }
    
    func fetchFish() -> [Fish] {
        let request: NSFetchRequest<Fish> = Fish.fetchRequest()
        do {
            return try moc.fetch(request)
        } catch {
            print("Error fetching fish entities: \(error)")
            return []
        }
    }
}

Feel free to modify to fit your needs.

  • Related