swift - 当 calloutAccessoryControlTapped 时更改传递到 "MapView: UIViewRepresentable"的 BindableObject 的值

标签 swift uikit mapkit swiftui

构建一个主要是 SwiftUI 应用程序...我正在尝试将父 View 中的 BindableObject 的值连接(绑定(bind))到 MapKit subview ,这样当点击注释(标注配件)时(小 ( i) 注释标签上的按钮...

enter image description here

...它将 $showDetails 的值更改为 true,该值连接到 View 层次结构中的“sheet(isPresented: $showDetails...)”,然后显示一个模式:

enter image description here

struct MapView: UIViewRepresentable {

    @Binding var mapSelected: Int
    @Binding var showDetails: Bool    // I WANT TO TOGGLE THIS WHEN CALLOUT ACCESSORY IS TAPPED

    @Binding var location: Location
    @Binding var previousLocation: Location

    @Binding var autoZoom: Bool
    @Binding var autoZoomLevel: Int

    class Coordinator: NSObject, MKMapViewDelegate {

        @Binding var showDetails: Bool

        init(showDetails: Binding<Bool>) {
            _showDetails = showDetails
        }

        func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
            guard let coordinates = view.annotation?.coordinate else { return }
            let span = mapView.region.span
            let region = MKCoordinateRegion(center: coordinates, span: span)
            mapView.setRegion(region, animated: true)
        }

        func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
            guard let annotation = annotation as? LocationAnnotation else { return nil }
            let identifier = "Annotation"
            var annotationView: MKMarkerAnnotationView? = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKMarkerAnnotationView
            if annotationView == nil {
                annotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: identifier)
                annotationView?.markerTintColor = UIColor(hex: "#00b4ffff")
                annotationView?.animatesWhenAdded = true
                annotationView?.canShowCallout = true
                annotationView?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
            } else {
                annotationView?.annotation = annotation
            }
            return annotationView
        }

    func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
            guard let loc = view.annotation as? LocationAnnotation else {
                    print("sorry")
                    return
            }
            print(self.showDetails)     // true, false, true, false, ...
            self.showDetails.toggle()   // THIS DOES TOGGLE, BUT THE VALUE IS NOT OBSERVED BY THE PARENT VIEW
        }
    }

    func makeCoordinator() -> Coordinator {
        return Coordinator(showDetails: $showDetails)
    }

    func makeUIView(context: Context) -> MKMapView {
            let map = MKMapView()
            map.delegate = context.coordinator
            return map
    }

    func updateUIView(_ uiView: MKMapView, context: Context) {
            switch mapSelected {
                    case 0:
                            uiView.mapType = .standard
                    default:
                            uiView.mapType = .hybrid
            }
            let currentRegion = uiView.region  // get the current region
            var span: MKCoordinateSpan
            var center: CLLocationCoordinate2D
            var newRegion: MKCoordinateRegion

            if currentRegion.span.latitudeDelta == 90.0 && currentRegion.span.longitudeDelta == 180.0 {   // INITIAL
                    span = MKCoordinateSpan(latitudeDelta: 18.0, longitudeDelta: 18.0)
                    center = CLLocationCoordinate2D(latitude: 54.5, longitude: -110)
                    newRegion = MKCoordinateRegion(center: center, span: span)
                    uiView.setRegion(newRegion, animated: true)
            }
            updateAnnotations(from: uiView)
    }

   // ...
}

我期望的是,当点击标注附件时,showDetails 会被切换(确实如此),但在父 View 中看不到任何效果 - 工作表未呈现。看来绑定(bind)没有发布其新状态。

我错过了什么/做错了什么?我发现将 UIKit 与 SwiftUI 集成起来很容易,然后很难,然后很容易,然后不可能。请帮忙!

最佳答案

事实证明,我找错地方了。问题不在于上面的代码(这是 100% 正确的),而在于容器 View 应该一直在监听 showDetails 的更改值,但是却没有不是因为我将 showDetails 传递给它的方式,例如

ContentView.swift

Footer(search: search,
    locationStore: self.locationStore,
    searchCoordinates: self.searchCoordinates,
    showDetails: self.showDetails) // NOT PASSED AS A BINDING...

Footer.swift

struct Footer: View {

@EnvironmentObject var settingsStore: SettingsStore

@ObservedObject var search: SearchTerm
@ObservedObject var locationStore: LocationStore
@ObservedObject var searchCoordinates: SearchCoordinates

@State var showDetails: Bool   // DECLARE LOCAL STATE, WILL NOT BE AWARE OF CHANGE TO showDetails FROM MapView

非常简单的修复:

ContentView.swift

Footer(search: search,
    locationStore: self.locationStore,
    searchCoordinates: self.searchCoordinates,
    showDetails: self.$showDetails)

Footer.swift

struct Footer: View {

@EnvironmentObject var settingsStore: SettingsStore

@ObservedObject var search: SearchTerm
@ObservedObject var locationStore: LocationStore
@ObservedObject var searchCoordinates: SearchCoordinates

@Binding var showDetails: Bool  

当不小心在传入变量上使用错误的属性包装器时,感觉像这样的错误在 SwiftUI 中很容易遇到(顺便说一句,这很棒)。编译器不会警告您,也没有运行时错误,只是......什么也没有发生。你和我一样,可能倾向于追逐转移注意力的代码,也就是完美的代码。

关于swift - 当 calloutAccessoryControlTapped 时更改传递到 "MapView: UIViewRepresentable"的 BindableObject 的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58513974/

相关文章:

ios - 在 Swift 中使用 MKLocalSearch 将一个图钉附加到用户位置,而不是蓝点

ios - 处理嵌套的 NSDictionary 键

ios - 如何在 Swift 中封装 UIViewController(如 UIAlertController)?

ios - Swift 3,无法在 viewcontroller 中创建多个函数?

xcode - 快速访问容器 View 子属性

ios - 为什么呈现 Controller 的 view.transform 在解雇时被重置?

swift - 在 swift 3 中使用扩展时使用未解析的标识符

swift - 如何通过点击“显示更多/显示更少”按钮来更新 TableView Controller 中的约束

ios - UIPanGestureRecognizer 为每个触摸点获取翻译

ios - 如何找到给定位置 iOS 中的所有餐厅