首页 > 解决方案 > SwiftUI - Mapkit - 绑定 mapkit 并在注释标注按钮上显示视图

问题描述

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

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

我的想法是创建地图:@State var map = MapView(displaySP: $displayStorePage)在正文之前但我不能这样做(错误:不能在属性初始化程序中使用实例成员'$displayStorePage';属性初始化程序在'self'可用之前运行)

非常欢迎任何帮助:)

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

所以在代码中,目标是displayStorePage从为注释单击激活的 Mapview 功能中切换 Mainviewfunc 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
    }
    
}

顶部菜单地图:

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
                }
    }
}

标签: swiftswiftuimapkit

解决方案


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 == $0.id
                })
                if idx != nil && $0.first != nil{
                    results[idx!] = $0.first!
                }
            }))
        }
    }
}
struct MapViewParent_Previews: PreviewProvider {
    static var previews: some View {
        MapViewParent()
    }
}

推荐阅读