SwiftUI - Mapkit - 绑定 mapkit 并在注释标注按钮上显示视图

SwiftUI - Mapkit - Binding mapkit and show view on annotation callout buttons

我有一个 SwiftUI 应用程序,我在其中显示了一张地图(使用由 UIViewRepresentable 制作的 Mapkit),其中显示了不同的注释。在每个注释上,我都实现了一个标注按钮,我想在按下按钮时完全更改主视图。

我的想法是使用主视图和 mapView 的 State/Binding 参数。这个想法可行,但我在使用地图视图的方式实现 State/Binding 时遇到了一些困难。困难在于我将地图用作第二个视图的参数,该视图包含与地图交互的菜单。我试图改变我使用地图视图的方式,但我没有找到任何解决方案...

我的想法是在正文之前创建地图:@State var map = MapView(displaySP: $displayStorePage) 但我做不到(错误:无法在 属性 初始值设定项中使用实例成员“$displayStorePage”;属性 'self' 可用之前的初始值设定项 运行)

非常欢迎任何帮助:)

编辑:附加代码显示,我尝试做最轻的版本。

所以在代码中,目标是从为注释激活的 Mapview 功能切换 Mainview 中的 displayStorePage 单击 func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl)

主视图:

import SwiftUI
import MapKit

struct MainView: View {
    @State var researchStore: String = ""
    @State var displayStorePage: Bool = false
    @State var map = MapView()
    
    var body: some View {
        ZStack{
            if displayStorePage == false {
                VStack{
                  map.frame(height: 400)
                }
            TopMapMenu(map: $map)
            }
            
            if displayStorePage == true {
                Text("This is a win !")
                }
            
            }
        }
    }

MapKit 视图:

import Combine

var storeLocation = [MKAnnotation]()
var newAnnotation = MKPointAnnotation()

let mapView = MKMapView()
var displayRedMarker: Bool = true
var displayStore: Bool = false


struct MapView: UIViewRepresentable {

    class Coordinator: NSObject, MKMapViewDelegate{
        var parent : MapView

        init(_ parent: MapView){
            self.parent = parent
        }
        
        
        func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
            
            var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "AnnotationView")
            
                annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "AnnotationView")
            
            let annotationLabel = UILabel(frame: CGRect(x: -25, y: 12, width: 105, height: 30))
            if displayRedMarker {
            annotationLabel.text = annotation.title!!
            } else {
            annotationLabel.text = "Annotation to be hidden"
            }
            
            //Other markers done there ....

            annotationView?.canShowCallout = true
            let btn = UIButton(type: .detailDisclosure)
                        annotationView?.rightCalloutAccessoryView = btn
            return annotationView
        }
        
        // Gestion click Annotation button
        func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
               // Here : Changing the Main View parameter : displayStorePage to true
        }
        
}
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    
    func makeUIView(context: Context) -> some UIView {
        
       
        let region = MKCoordinateRegion(
            center: CLLocationCoordinate2D(latitude: 47.510053, longitude: 6.798374),
            span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
        
        mapView.delegate = context.coordinator
        mapView.showsUserLocation = true
        mapView.setRegion(region, animated: false)
        
        mapView.addAnnotation(AddAnnotations())
        
        return mapView
    }
    
    func updateUIView(_ uiView: UIViewType, context: Context) {
        
    }
 
    func AddAnnotations() -> MKAnnotation {
        
        newAnnotation = MKPointAnnotation()
        newAnnotation.title = "Title1"
        newAnnotation.coordinate = CLLocationCoordinate2D(latitude: 47.510053, longitude: 6.798374)
        
        return newAnnotation
    }
    
}

TopMenuMap:

struct TopMapMenu: View {
    
     @Binding var map : MapView
     @State var searching: Bool = false
     @State var redStore: Bool = true
     @State var storeFound : Bool = false
    
    var body: some View {
            
                HStack{
                    VStack(alignment: .leading, spacing: 10){
                        
                        Button(action: {
                            displayRedMarker.toggle()
                         //   map.DeleteMarkers()
                         //   map.AddMarkers()
                            redStore = displayRedMarker
                        }) {
                            HStack {
                            if redStore { Image(systemName: "heart.fill") }
                            else { Image(systemName: "heart") }
                                
                                Text("Restaurant")
                                }
                            Spacer()
                                .background(Color(.white))
                            }
                        // Other Buttons(Pins) are display here
                    }// End VStack Pin stores
                }
    }
}

好吧,我尝试了很多东西,但对我来说没有任何效果,但我找到了一个可行的解决方案,但是......它看起来不像是正确的方法......我没想到 TopMapMenu 会积极更改 VStack 中的 Mapkit,但显然它确实如此。我 post 新 post 如果它可以帮助某人或者如果有人有更好的解释。

struct MainView: View {
    @State var displayStorePage: Bool = false
    @State var map = MapView(displaySP: Binding.constant(false))
    
    var body: some View {   
        ZStack{
         
            if displayStorePage == false {
                VStack{
                    MapView(displaySP: $displayStorePage)
                    Spacer()
                }
                
            TopMapMenu(map: $map)
            }
            
            if displayStorePage == true {
                StorePage()
            }
            
        }
    }

对于 Mapkit:

struct MapView: UIViewRepresentable {
 
 @Binding var displayStorePage: Bool 
    
    init(displaySP: Binding<Bool>) {
        self._displayStorePage = displaySP 
        loadAnnotation()
    }
...
import SwiftUI
import MapKit
class Store: Identifiable, ObservableObject {
    let id: UUID = UUID()
    @Published var title: String?
    @Published var latitude: Double
    @Published var longitude: Double
    @Published var displayStoreImage: Bool
    @Published var displayRedMarker: Bool
    @Published var redStore: Bool
    init(title: String, latitude: Double , longitude: Double, displayStoreImage: Bool, displayRedMarker: Bool, redStore: Bool ) {
        self.title = title
        self.latitude = latitude
        self.longitude = longitude
        self.displayRedMarker = displayRedMarker
        self.displayStoreImage = displayStoreImage
        self.redStore = redStore
        
    }
}
extension Store{
    var coordinate: CLLocationCoordinate2D{
        get{
            return CLLocationCoordinate2D(latitude: self.latitude, longitude: self.longitude)
        }
    }
}

struct MapViewParent: View {
    @State var selectedListing: Store?
    @State var results: [Store] = [Store(title: "Point 1 title", latitude: 38.3 , longitude: 123.2, displayStoreImage: true, displayRedMarker: true, redStore: false ), Store(title: "Point 2 title", latitude: 38.4 , longitude: 123.1, displayStoreImage: true, displayRedMarker: false, redStore: true )
    ]
    var body: some View {
        VStack{
            Button("clear-storepage", action: {
                selectedListing = nil
            })
            if selectedListing != nil{
                TopMapMenu(results: $results, selectedListing: $selectedListing)
            }else{
                MapViewAnnotations(selectedListing: $selectedListing, results: $results)
            }
        }
    }
}
struct MapViewAnnotations: View {
    @State var region: MKCoordinateRegion = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 38.3 , longitude: 123.2 ), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
    
    @Binding var selectedListing: Store?
    @Binding var results: [Store]
    
    var body: some View {
        Map(coordinateRegion: $region, annotationItems: results, annotationContent: {
            listing in
            MapAnnotation(coordinate: listing.coordinate, content: {
                Button(action: {
                    self.selectedListing = listing as Store?
                    self.region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: listing.coordinate.latitude , longitude: listing.coordinate.longitude ), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
                }, label: {
                    if listing.displayStoreImage{
                        if listing.displayRedMarker{
                            Image(systemName: "mappin.circle.fill").foregroundColor(.red).contentShape(Circle())
                        }else{
                            Image(systemName: "mappin").foregroundColor(.green).contentShape(Circle())
                        }
                    }
                    else{
                        Image(systemName: "mappin.circle.fill").foregroundColor(.red).contentShape(Circle())
                    }
                }).buttonStyle(PlainButtonStyle())
            })
        })
        
    }
}

struct TopMapMenu: View {
    @Binding var results: [Store]
    @Binding var selectedListing: Store?
    var body: some View {
        List{
            ForEach(results, id: \.id){store in
                StoreView(store: store, selectedListing: $selectedListing, results: $results)
            }
        }
    }
}
struct StoreView: View {
    @ObservedObject var store: Store
    @Binding var selectedListing: Store?
    @Binding var results: [Store]
    var body: some View{
        HStack{
            VStack(alignment: .leading, spacing: 10){
                Button(action: {
                    store.displayRedMarker.toggle()
                    store.redStore = store.displayRedMarker
                }, label: {
                    HStack {
                        if store.redStore {
                            Image(systemName: "heart.fill")
                        }
                        else { Image(systemName: "heart") }
                        
                        Text("Restaurant")
                    }
                    Spacer()
                        .background(Color(.white))
                }
                )}
            MapViewAnnotations(selectedListing: $selectedListing, results: Binding(get: {
                return [store]
            }, set: {
                let idx = results.firstIndex(where: {
                    store.id == [=10=].id
                })
                if idx != nil && [=10=].first != nil{
                    results[idx!] = [=10=].first!
                }
            }))
        }
    }
}
struct MapViewParent_Previews: PreviewProvider {
    static var previews: some View {
        MapViewParent()
    }
}