SwiftUI MapKit UIViewRepresentable Невозможно представить оповещение - PullRequest
1 голос
/ 22 февраля 2020

У меня есть приложение SwiftUI, где пара представлений - это карты MapKit, созданные с помощью UIViewRepresentable. У меня есть пользовательские аннотации для точек интереса и я использую правую и левую кнопки выноски для дальнейших действий. Справа я просто хочу отобразить информацию о путевой точке. Слева я хочу поднять предупреждение с выбором дальнейших действий - например, вставить новую точку аннотации. Перед SwiftUI я просто поднял предупреждение и сделал оба вышеперечисленных. Но из того, что я могу сказать, в версиях UIViewRepresentable отсутствует self.present. Поэтому я не смог представить оповещения.

Просто для эксперимента - я прикрепил код оповещения SwiftUI к представлению SwiftUI, которое вызывает MapView. Используя наблюдаемые логические значения, я действительно могу поднять оба этих предупреждения. Это кажется странным для меня, но, возможно, код для предупреждений, связанных с глобальными свойствами, может быть где угодно

Моя первая попытка: (структура - 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
    }
}

Полагаю, если это безопасно, я мог бы заставить его работать - но это кажется более сложным, чем должно быть.

Это идея:

enter image description here

Будем благодарны за любые рекомендации: Xcode Version 11.3.1 (11C504)

Ответы [ 2 ]

3 голосов
/ 22 февраля 2020

Я потратил на это час, я новичок в SwiftUI и прыгнул в него, чтобы просто ответить на несколько простых вопросов.

Один из способов сделать то, что вы хотите, - это использовать Bool (@State и @Binding).

Вам также нужно иметь View вместо непосредственного использования вашего UIViewRepresentable в вашем SceneDelegate. Потому что именно здесь вы будете связывать свои 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 должен go вот так:

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 один.

enter image description here

0 голосов
/ 22 февраля 2020

Гленн, да. Это на самом деле так, как я сделал это выше. Однако я использовал ObservedObjects, и я думаю, что ваш подход с @State и @Binding лучше. Также я не хочу показывать карту условно - только аннотации соединений. По какой-то причине я не смог добавить комментарий к вашему ответу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...