在 SwiftUI 中点击 MKMapView

问题描述 投票:0回答:3

我在 SwiftUI 应用程序中有一张地图。它已经发挥到一定程度了;但现在我希望能够点击它并知道点击的纬度和经度。这是当前代码:

import SwiftUI
import MapKit

struct MapView: UIViewRepresentable {
    @Binding var centerCoordinate: CLLocationCoordinate2D
    
    func makeUIView(context: Context) -> MKMapView {
        let mapView = MKMapView()
        mapView.delegate = context.coordinator
        
        let gRecognizer = UITapGestureRecognizer(target: context.coordinator,
                                action: #selector(Coordinator.tapHandler(_:)))
        mapView.addGestureRecognizer(gRecognizer)
        return mapView
    }

    func updateUIView(_ view: MKMapView, context: Context) {
        //print(#function)
    }

    func makeCoordinator() -> Coordinator {
        return Coordinator(self)
    }

    class Coordinator: NSObject, MKMapViewDelegate {
        var parent: MapView

        init(_ parent: MapView) {
            self.parent = parent
        }

        let gRecognizer = UITapGestureRecognizer(target: self,
                                                 action: #selector(tapHandler(_:)))
        
        @objc func tapHandler(_ gesture: UITapGestureRecognizer) {
            print(#function)
            .... get useful information here ...
        }
    }
}

在这种状态下,我可以看到我何时点击,但我没有获得我需要的信息(即点击的坐标)。 在搜索网络后,我尝试了代码的一些变体。 目前还无法正常工作。任何有关出行方式的相关提示都将非常受欢迎。

swift swiftui mkmapview uitapgesturerecognizer uiviewrepresentable
3个回答
10
投票

我也遇到过类似的情况,我就是这么做的。 我制作了 Coordinator UIGestureRecognizerDelegate,并确保将 gRecognizer delegate 设置为它,并将其添加到地图中。比如:

struct MapView: UIViewRepresentable {
@Binding var centerCoordinate: CLLocationCoordinate2D

let mapView = MKMapView()

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

func updateUIView(_ view: MKMapView, context: Context) {
    //print(#function)
}

func makeCoordinator() -> Coordinator {
    return Coordinator(self)
}

class Coordinator: NSObject, MKMapViewDelegate, UIGestureRecognizerDelegate {
    var parent: MapView

    var gRecognizer = UITapGestureRecognizer()

    init(_ parent: MapView) {
        self.parent = parent
        super.init()
        self.gRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapHandler)) 
        self.gRecognizer.delegate = self
        self.parent.mapView.addGestureRecognizer(gRecognizer)
    }

    @objc func tapHandler(_ gesture: UITapGestureRecognizer) {
        // position on the screen, CGPoint
        let location = gRecognizer.location(in: self.parent.mapView)
        // position on the map, CLLocationCoordinate2D
        let coordinate = self.parent.mapView.convert(location, toCoordinateFrom: self.parent.mapView)
        
    }
}
}

0
投票

当我尝试为在 macOS 上运行的 SwiftUI 应用程序获取地图上的点击位置时,我遇到了类似的问题,所以显然,我必须使用

NSViewRepresentable
而不是
UIViewRepresentable

@workingdog 的回答很有帮助;这是我的 macOS 版本,它在

Text
视图中显示单击的位置,Z 堆叠在地图顶部:

struct MapViewRepresentable: NSViewRepresentable {
  @Binding var clickedCoordinate: CLLocationCoordinate2D
  var initialLocation: CLLocationCoordinate2D
  var initialSpan: MKCoordinateSpan

  let mapView = MKMapView()
  
  func makeNSView(context: Context) -> MKMapView {
    mapView.preferredConfiguration = MKHybridMapConfiguration(elevationStyle: .realistic)
    mapView.region = MKCoordinateRegion(center: initialLocation, span: initialSpan)
    mapView.delegate = context.coordinator
    return mapView
  }
  
  func updateNSView(_ nsView: MKMapView, context: Context) {
    
  }
  
  func makeCoordinator() -> Coordinator {
    return Coordinator(self)
  }
  
  class Coordinator: NSObject, MKMapViewDelegate, NSGestureRecognizerDelegate {
    @State var parent: MapViewRepresentable
    
    var gRecognizer = NSClickGestureRecognizer()
    
    init(_ parent: MapViewRepresentable) {
      self.parent = parent
      super.init()
      self.gRecognizer = NSClickGestureRecognizer(target: self, action: #selector(tapHandler))
      self.gRecognizer.delegate = self
      self.parent.mapView.addGestureRecognizer(gRecognizer)
    }
    
    @objc func tapHandler(_ gesture: NSClickGestureRecognizer) {
      let location = gesture.location(in: self.parent.mapView)
      let coordinate = self.parent.mapView.convert(location, toCoordinateFrom: self.parent.mapView)
      parent.clickedCoordinate = coordinate
    }
  }
}

struct MapView: View {
  private static let initialLocation = CLLocationCoordinate2D(latitude: 51.5, longitude: 0.0)  // London, UK
  private static let initialSpan = MKCoordinateSpan(latitudeDelta: 1.0, longitudeDelta: 1.0)

  @State private var clickedCoordinate: CLLocationCoordinate2D = Self.initialLocation

  var body: some View {
    ZStack {
      MapViewRepresentable(clickedCoordinate: $clickedCoordinate, initialLocation: Self.initialLocation, initialSpan: Self.initialSpan)
      HStack {
        Spacer()
        VStack(alignment: .trailing) {
          Spacer()
          Text("Clicked - Lat: \(latAsStr(clickedCoordinate.latitude)) Lon: \(lonAsStr(clickedCoordinate.longitude))")
            .background(.white).opacity(0.75)
            .foregroundColor(.black)
            .textSelection(.disabled)
            .padding(5)
        }
      }
    }
  }
  
  func latAsStr(_ lat: Double) -> String { 
    // convert lat to string
  }
  
  func lonAsStr(_ lon: Double) -> String {
    // convert lon to string
  }

}

enter image description here


0
投票

我也想添加点击事件,但保留默认的

Map
手势存在很多问题。我想出了一个处理
onTapGesture
的函数,该函数在 iOS 16 及更高版本中可用,对于旧版本,创建一个手势,然后返回点击的位置。

import SwiftUI

extension View {

    func onTapGestureButCompatible(
        coordinateSpace: CoordinateSpace = .local,
        perform action: @escaping (CGPoint) -> Void
    ) -> some View {
        if #available(iOS 16.0, *) {
            return onTapGesture(count: 1, coordinateSpace: coordinateSpace, perform: action)
        } else {
        
            let drag = DragGesture(minimumDistance: 0,
                                   coordinateSpace: coordinateSpace)
                                .onEnded { value in
                                    action(value.location)
                                }
        
            return simultaneousGesture(TapGesture().sequenced(before: drag))
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.