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.