简体   繁体   English

SwiftUI MapKit UIViewRepresentable 无法显示警报

[英]SwiftUI MapKit UIViewRepresentable Can't present Alert

I have a SwiftUI app where a couple of views are MapKit maps made with UIViewRepresentable.我有一个 SwiftUI 应用程序,其中有几个视图是使用 UIViewRepresentable 制作的 MapKit 地图。 I have custom annotations for the points of interest and use both the right and left callout buttons for further action.我有兴趣点的自定义注释,并使用左右标注按钮进行进一步操作。 On the right I simply want to display information about the waypoint.在右侧,我只想显示有关航点的信息。 On the left I want to raise an alert with a choice of further actions - for example, insert a new annotation point.在左侧,我想通过选择进一步的操作来发出警报 - 例如,插入一个新的注释点。 Before SwiftUI I just raised an alert and did both of the above.在 SwiftUI 之前,我只是提出了一个警报并执行了上述两项操作。 But from what I can tell, there is no self.present on the UIViewRepresentable versions.但据我所知, UIViewRepresentable 版本上没有 self.present 。 Hence I have not been able to present alerts.因此,我无法显示警报。

Just for an experiment - I attached SwiftUI alert code to the SwiftUI view that calls the MapView.仅用于实验 - 我将 SwiftUI 警报代码附加到调用 MapView 的 SwiftUI 视图。 Using Observable booleans I can indeed raise both those alerts.使用 Observable 布尔值,我确实可以同时发出这些警报。 That seems strange to me but maybe the code for alerts bound to global properties can be ANYWHERE.这对我来说似乎很奇怪,但也许绑定到全局属性的警报代码可以在任何地方。

My first attempt: (the struct is DetailMapView)我的第一次尝试:(结构是 DetailMapView)

func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {

    if control == view.leftCalloutAccessoryView {
        guard let tappedLocationCoord = view.annotation?.coordinate else {return}
        let tappedLocation = CLLocation(latitude: tappedLocationCoord.latitude, longitude: tappedLocationCoord.longitude)

        let ac = UIAlertController(title: nil, message: nil, preferredStyle: .alert)

        let deleteAction = UIAlertAction(title: "Delete Waypoint", style: .destructive) { (action) in
            //some stuff
        }

        let insertAction = UIAlertAction(title: "Insert Waypoint After This", style: .default) { (action) in
            //some stuff
        }

        let cancelAction = UIAlertAction(title: "Cancel", style: .default) { (action) in
            //some stuff
        }//cancelAction

        ac.addAction(deleteAction)
        ac.addAction(insertAction)
        ac.addAction(cancelAction)

        //tried adding self, referencing parent - always error saying
        //the object does not have a .present
        mapView.present(ac, animated: true)

    } else if control == view.rightCalloutAccessoryView {
        //more of the same
    }
}//annotationView

I then removed the alert code and added:然后我删除了警报代码并添加了:

parent.userDefaultsManager.shouldShowAnnotationEditMenu.toggle()

And I changed the calling screen to:我将呼叫屏幕更改为:

@ObservedObject var userDefaultsManager: UserDefaultsManager
var aTrip: Trip?

var body: some View {

    VStack {
        Text(aTrip?.name ?? "Unknown Map Name")
            .padding(.top, -50)
            .padding(.bottom, -20)

        DetailMapView(aTrip: aTrip, userDefaultsManager: userDefaultsManager)
            .padding(.top, -20)
            .alert(isPresented: $userDefaultsManager.shouldShowAddress) {
                //Alert(title: Text("\(aTrip?.name ?? "No") Address"),
                Alert(title: Text(self.userDefaultsManager.approximateAddress),
                      message: Text("This is the approximate street address."),
                      dismissButton: .default(Text("Got it!")))
            }//.alert shouldShowAddress

        Text("This is the view where the trip information will be displayed.")
            .multilineTextAlignment(.center)
        .alert(isPresented: $userDefaultsManager.shouldShowAnnotationEditMenu) {
            Alert(title: Text("Edit Annotations"),
                  message: Text("Choose this to insert an Annotation."),
                  dismissButton: .default(Text("Got it!")))
        }//.alert shouldShowAddress
    }
}

I guess if this is safe I could make it work - but it seems more complicated that it should be.我想如果这是安全的,我可以让它工作 - 但它应该看起来更复杂。

This is the idea:这是想法:

在此处输入图片说明

Any guidance would be appreciated: Xcode Version 11.3.1 (11C504)任何指导将不胜感激:Xcode 版本 11.3.1 (11C504)

I spent an hour for this, I'm new to SwiftUI, and I jumped into it just to answer some easy questions.我为此花了一个小时,我是 SwiftUI 的新手,我跳进去只是为了回答一些简单的问题。

One way to do what you want is to use Bool ( @State and @Binding ).做你想做的一种方法是使用Bool@State@Binding )。

You also need to have a View rather than directly use your UIViewRepresentable in your SceneDelegate .您还需要有一个View而不是直接在UIViewRepresentable中使用SceneDelegate Because that is where you will chain your alert .因为那是您链接alert

Like so:像这样:

struct MainView: View {
    @State var text = ""
    @State var showingAlert: Bool

    var body: some View {
        VStack {
            MapView(showingAlert: self.$showingAlert)
                .alert(isPresented: $showingAlert) { () -> Alert in
                    print("SHOWING ALERT BODY: --> \($showingAlert.wrappedValue)")
                    return Alert(title: Text("Important message"), message: Text("Go out and have a girlfriend!"), dismissButton: .default(Text("Got it!")))
            }
        }
    }
}

and then your MapView should go like this:然后你的MapView应该是这样的:

struct MapView: UIViewRepresentable {

    let landmarks = LandmarkAnnotation.requestMockData()

    @Binding var showingAlert: Bool

    func makeCoordinator() -> MapViewCoordinator {
        MapViewCoordinator(mapView: self, showingAlert: self.$showingAlert)
    }

    /**
     - Description - Replace the body with a make UIView(context:) method that creates and return an empty MKMapView
     */
    func makeUIView(context: Context) -> MKMapView {
        MKMapView(frame: .zero)
    }

    func updateUIView(_ view: MKMapView, context: Context){
        //If you changing the Map Annotation then you have to remove old Annotations
        //mapView.removeAnnotations(mapView.annotations)
        view.delegate = context.coordinator
        view.addAnnotations(landmarks)
    }
}

struct MapView_Previews: PreviewProvider {
    static var previews: some View {
        MapView(showingAlert: Binding<Bool>.constant(true))
    }
}

Finally, in your MapViewCoordinator (I suppose you have this class, this is the one that implements the delegate methods of the MKMapViewDelegate .).最后,在您的MapViewCoordinator (我想您有这个类,这是实现MKMapViewDelegate的委托方法的类。)。

/*
 Coordinator for using UIKit inside SwiftUI.
 */
class MapViewCoordinator: NSObject, MKMapViewDelegate {

    var mapViewController: MapView!
    @Binding var showAlert: Bool

    init(mapView: MapView, showingAlert: Binding<Bool>) {

        self.mapViewController = mapView
        self._showAlert = showingAlert
        super.init()
    }

    func mapView(_ mapView: MKMapView, viewFor
        annotation: MKAnnotation) -> MKAnnotationView?{
        //Custom View for Annotation
        let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "customView")
        annotationView.canShowCallout = true
        //Your custom image icon
        annotationView.image = UIImage(named: "locationPin")
        return annotationView
    }

    func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
        print("calloutAccessoryControlTapped")
    }

    func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
        print("didSelect")

        self.showAlert = true
    }
}

So as you can see, I just make use of the Bool flag.如您所见,我只是使用 Bool 标志。 Especially the @Binding one.特别是@Binding之一。

在此处输入图片说明

Yes, Glenn.是的,格伦。

This is actually the way I did it.这实际上是我做的方式。 I had used ObservedObjects .我曾经使用过ObservedObjects However I think your approach with @State and @Binding is better.但是我认为您使用@State@Binding方法更好。 Also, I do not want to show the map conditionally - just want to show annotation connections.另外,我不想有条件地显示地图 - 只想显示注释连接。 For some reason I was not able to add a comment to your answer so replying here.出于某种原因,我无法在您的答案中添加评论,因此在此处回复。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM