简体   繁体   English

SwiftUI Map 覆盖没有 UIViewRepresentable

[英]SwiftUI Map Overlays without UIViewRepresentable

I try to create Map view with annotations and overlays using only iOS 14 SwiftUI Map.我尝试使用仅使用 iOS 14 SwiftUI Z46F3EA056CAA3026B91FEA3F7 I managed to create annotations but I am not able to create visible overlays because of lack of MKOverlayRenderer from MKMapView delegate.我设法创建了注释,但由于 MKMapView 委托缺少 MKOverlayRenderer,我无法创建可见的叠加层。 How can I attach to Map view delegate so it can run its method如何附加到 Map 视图委托,以便它可以运行其方法

    Map(coordinateRegion: $region, showsUserLocation: true, userTrackingMode: $userTrackingMode, annotationItems: annotations)) { annotation in
        MapAnnotation(coordinate: CLLocationCoordinate2DMake(annotation.latitude, annotation.longitude),
                      anchorPoint: CGPoint(x: 0.5, y: 0.7)) {
            NavigationLink(destination: DetailsView()) {
                VStack {
                    Image(systemName: "mappin")
                        .font(.title)
                        .foregroundColor(.red)
                    Text(annotation.name.split(separator: " ").joined(separator: "\n"))
                        .font(.caption)
                        .bold()
                        .multilineTextAlignment(.center)
                        .foregroundColor(.black)
                        .padding(2)
                        .background(RoundedRectangle(cornerRadius: 4).fill(Color.white.opacity(0.8)))
                        .layoutPriority(1)
                }
            }
        }
    }
    .addOverlays(mapOverlays, delegate: mapDelegate)

and Map extension method:和 Map 扩展方法:

extension Map {
    func addOverlays(_ overlays: [MKOverlay], delegate: MKMapViewDelegate) -> some View {
        // MKMapView.appearance().delegate = delegate // THIS DOES NOT WORK AT ALL

        MKMapView.appearance().addOverlays(overlays)

        return self
    }
}

and delegate:并委托:

class MapDelegate: NSObject, MKMapViewDelegate {
    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        if overlay is MKCircle {
            let circle = MKCircleRenderer(overlay: overlay)
            circle.strokeColor = .systemRed
            circle.fillColor = .systemRed
            circle.alpha = 0.2
            circle.lineWidth = 1

            return circle
        }
        return MKOverlayRenderer()
    }
}

Definitely not possible, SwiftUI Map does not yet support overlays.绝对不可能,SwiftUI Map 还不支持覆盖。 Using UIViewRepresentsable is easy, see SO example here:使用 UIViewRepresentsable 很容易,请参见此处的示例:
Swiftui how to use MKOverlayRenderer? Swiftui 如何使用MKOverlayRenderer? using the following code:使用以下代码:

    struct MapView: UIViewRepresentable {
    @Binding var route: MKPolyline?
    let mapViewDelegate = MapViewDelegate()

    func makeUIView(context: Context) -> MKMapView {
        MKMapView(frame: .zero)
    }

    func updateUIView(_ view: MKMapView, context: Context) {
        view.delegate = mapViewDelegate                          // (1) This should be set in makeUIView, but it is getting reset to `nil`
        view.translatesAutoresizingMaskIntoConstraints = false   // (2) In the absence of this, we get constraints error on rotation; and again, it seems one should do this in makeUIView, but has to be here
        addRoute(to: view)
    }
}

private extension MapView {
    func addRoute(to view: MKMapView) {
        if !view.overlays.isEmpty {
            view.removeOverlays(view.overlays)
        }

        guard let route = route else { return }
        let mapRect = route.boundingMapRect
        view.setVisibleMapRect(mapRect, edgePadding: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10), animated: true)
        view.addOverlay(route)
    }
}

class MapViewDelegate: NSObject, MKMapViewDelegate {
    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        let renderer = MKPolylineRenderer(overlay: overlay)
        renderer.fillColor = UIColor.red.withAlphaComponent(0.5)
        renderer.strokeColor = UIColor.red.withAlphaComponent(0.8)
        return renderer
    }
}

Probably this is a pretty late answer, but could be useful to someone else.可能这是一个很晚的答案,但可能对其他人有用。

After trying a few approaches, I came to this solution (btw, I would say that's not even close to a fancy code... but it works) .在尝试了几种方法之后,我找到了这个解决方案(顺便说一句,我会说这甚至不接近花哨的代码......但它确实有效)

First, you have to create a class that implements the MKMapViewDelegate (in my case I needed to show an overlay with a custom theme on the Map , but you can use it to show a Polyline or a Polygon as well).首先,您必须创建一个实现MKMapViewDelegate的 class (在我的情况下,我需要在Map上显示具有自定义主题的叠加层,但您也可以使用它来显示折线或多边形)。

class MapCustomDelegate: NSObject, MKMapViewDelegate {
    var parent: MKMapView
    
    init(_ parent: MKMapView) {
        self.parent = parent
    }
    
    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
      if let tileOverlay = overlay as? MKTileOverlay {
        let renderer = MKTileOverlayRenderer(overlay: tileOverlay)
        return renderer
      }
      return MKOverlayRenderer()
    }
}

Then in the view you have to implement the delegate using the MKMapView.appearance() instance, like this:然后在视图中,您必须使用MKMapView.appearance()实例来实现委托,如下所示:

struct MapCustomView: View {
    // Properties
    @StateObject private var viewModel: MapViewModel = MapViewModel()
    private let mapAppearanceInstance = MKMapView.appearance()
    private var mapCustomDelegate: MapCustomDelegate = MapCustomDelegate(MKMapView.appearance())                     
    ..
                    
    var body: some View {
       VStack {   
          Map(coordinateRegion: $viewModel.mapRegion,
              annotationItems: viewModel.items) { item in
                    
              MapAnnotation(coordinate: item.location) {
                ..
              }
          }
          .onAppear {
             // viewModel.mapOverlay -> MKTileOverlay
             self.mapAppearanceInstance.delegate = mapCustomDelegate                         
             self.mapAppearanceInstance.addOverlay(viewModel.mapOverlay)
          }
     }
}

And it should work.它应该工作。 Hope this helps:)希望这可以帮助:)

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

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