【问题标题】:SwiftUI MapKit UIViewRepresentable Can't present AlertSwiftUI MapKit UIViewRepresentable 无法显示警报
【发布时间】:2020-02-22 03:09:11
【问题描述】:

我有一个 SwiftUI 应用程序,其中有几个视图是使用 MapKit 制作的地图 UIView 可表示的。我有兴趣点和使用的自定义注释 右侧和左侧的标注按钮,用于进一步操作。在右边我只是想要 显示有关航点的信息。在左边我想发出警报 进一步操作的选择 - 例如,插入一个新的注释点。前 SwiftUI 我刚刚提出了一个警报,并做了上述两个。但据我所知, UIViewRepresentable 版本上没有 self.present。因此我没有 能够显示警报。

只是为了一个实验 - 我将 SwiftUI 警报代码附加到 SwiftUI 视图中 调用 MapView。使用 Observable 布尔值,我确实可以同时发出这两个警报。 这对我来说似乎很奇怪,但也许是绑定到全局属性的警报代码 可以在任何地方。

我的第一次尝试:(结构是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

然后我删除了警报代码并添加了:

parent.userDefaultsManager.shouldShowAnnotationEditMenu.toggle()

然后我将呼叫屏幕更改为:

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

我想如果这是安全的,我可以让它工作 - 但它似乎更复杂 应该是。

这就是想法:

任何指导将不胜感激:Xcode 版本 11.3.1 (11C504)

【问题讨论】:

    标签: ios xcode mapkit swiftui uiviewrepresentable


    【解决方案1】:

    我为此花了一个小时,我是 SwiftUI 的新手,我跳进去只是为了回答一些简单的问题。

    一种方法是使用Bool@State@Binding)。

    您还需要有一个View,而不是直接在您的SceneDelegate 中使用您的UIViewRepresentable。因为那是你链接alert的地方。

    像这样:

    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!")))
                }
            }
        }
    }
    

    然后你的 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))
        }
    }
    

    最后,在你的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
        }
    }
    

    如您所见,我只是使用了 Bool 标志。尤其是@Binding 一个。

    【讨论】:

      【解决方案2】:

      是的,格伦。

      这实际上是我的做法。我用过ObservedObjects。但是我认为您使用@State@Binding 的方法更好。 此外,我不想有条件地显示地图 - 只想显示注释连接。 由于某种原因,我无法对您的答案添加评论,所以在这里回复。

      【讨论】:

        猜你喜欢
        • 2021-08-11
        • 1970-01-01
        • 1970-01-01
        • 2021-10-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多