【问题标题】:Update MapView with current location on SwiftUI使用 SwiftUI 上的当前位置更新 MapView
【发布时间】:2020-05-01 01:47:59
【问题描述】:

尝试更新 Project 14 of 100daysOfSwiftUI 的地图视图以显示我当前的位置,问题我无法放大移动

我有这段代码,我将 @Binding var currentLocation : CLLocationCoordinate2Dview.setCenter(currentLocation, animated: true) 添加到我的 MapView 中,所以我有一个按钮可以发送该值,并且视图实际上移动到该位置的速度非常慢,但我可以再离开

import SwiftUI
import MapKit

struct MapView: UIViewRepresentable {

    @Binding var centerCoordinate: CLLocationCoordinate2D
    @Binding var selectedPlace: MKPointAnnotation?
    @Binding var showingPlaceDetails: Bool
    @Binding var currentLocation : CLLocationCoordinate2D

    var annotations: [MKPointAnnotation]

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

    func updateUIView(_ view: MKMapView, context: Context) {

        if annotations.count != view.annotations.count {
            view.removeAnnotations(view.annotations)
            view.addAnnotations(annotations)
        }

        view.setCenter(currentLocation, animated: true)

    }

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

 class Coordinator: NSObject, MKMapViewDelegate{

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

    func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) {
         parent.centerCoordinate = mapView.centerCoordinate
     }

     func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
         let identifier = "PlaceMark"
         var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
         if annotationView == nil {
             annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
             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 placemark = view.annotation as? MKPointAnnotation else {return}
         parent.selectedPlace = placemark
         parent.showingPlaceDetails = true

     }

    }
}

这是我的 swiftUI 视图

...
    @State private var currentLocation = CLLocationCoordinate2D()

    var body: some View {
        ZStack{

            MapView(centerCoordinate: $centerCoordinate, selectedPlace: $selectedPlace, showingPlaceDetails: $showingPlaceDetails, currentLocation: $currentLocation ,  annotations: locations)
           // MapView(centerCoordinate: $centerCoordinate, selectedPlace: $selectedPlace, showingPlaceDetails: $showingPlaceDetails, annotations: locations)
                .edgesIgnoringSafeArea(.all)
            VStack{
                Spacer()
                HStack{
                    Spacer()
                    Button(action: {
                        self.getCurrentLocation()
                    }){
                        ButtonIcon(icon: "location.fill")
                    }
                }
                .padding()
            }
        }
        .onAppear(perform: getCurrentLocation)
    }

    func getCurrentLocation() {

        let lat = locationManager.lastLocation?.coordinate.latitude ?? 0
        let log = locationManager.lastLocation?.coordinate.longitude ?? 0

        self.currentLocation.latitude = lat
        self.currentLocation.longitude = log

    }
    ...

更新

感谢支持我使用这个课程拨打locationManager.requestWhenInUseAuthorization()

import Foundation
import CoreLocation
import Combine

class LocationManager: NSObject, ObservableObject {

    override init() {
        super.init()
        self.locationManager.delegate = self
        self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
        self.locationManager.requestWhenInUseAuthorization()
        self.locationManager.startUpdatingLocation()
    }

    @Published var locationStatus: CLAuthorizationStatus? {
        willSet {
            objectWillChange.send()
        }
    }

    @Published var lastLocation: CLLocation? {
        willSet {
            objectWillChange.send()
        }
    }

    var statusString: String {
        guard let status = locationStatus else {
            return "unknown"
        }

        switch status {
        case .notDetermined: return "notDetermined"
        case .authorizedWhenInUse: return "authorizedWhenInUse"
        case .authorizedAlways: return "authorizedAlways"
        case .restricted: return "restricted"
        case .denied: return "denied"
        default: return "unknown"
        }

    }

    let objectWillChange = PassthroughSubject<Void, Never>()

    private let locationManager = CLLocationManager()
}

extension LocationManager: CLLocationManagerDelegate {

    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        self.locationStatus = status
        print(#function, statusString)
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else { return }
        self.lastLocation = location
        print(#function, location)
    }

}

当我按下按钮时,我只想将我的地图视图集中在我当前的位置上

【问题讨论】:

  • 顺便说一句,如果你原谅我指出这一点,但如果下次你能提供一个完整的例子就好了(例如这个lastLocation属性是什么?;一些@ 987654328@ 属性丢失;等等)并删除不必要的东西(例如,我们真的需要ButtonIcon 对象吗?我们需要注释相关的代码吗?)。我们通常想做的第一件事,尤其是遇到如此复杂的问题,就是尝试重现您的问题,而您却很难做到这一点。这里不用担心,但为了将来参考......
  • @Rob 谢谢我刚刚添加了我管理位置权限的类,lastLocation 是CLLocation?

标签: swift swiftui mkmapview


【解决方案1】:

你从来没有在哪里打电话给locationManager.requestWhenInUseAuthorization()。当我这样做时(当然,确保Info.plist 有一个NSLocationWhenInUseUsageDescription 的条目),它正确地更新了位置。

例如

func getCurrentLocation() {
    if CLLocationManager.authorizationStatus() == .notDetermined {
        locationManager.requestWhenInUseAuthorization()
    }
    if let coordinate = locationManager.location?.coordinate {
        currentLocation = coordinate
    }
}

现在,这只是一个快速而肮脏的修复,以证明它有效。但这不太对,因为第一次调用getCurrentLocation时,如果它必须向用户请求许可,它是异步执行的,这意味着当你到达lastLocation时它还没有位置在您的实施中行。这是一次性的事情,但仍然不能接受。如果需要,你会想要你的 CLLocationManagerDelegate 更新 currentLocation。但希望您在这里有足够的信息来诊断为什么CLLocationManager 没有正确捕获您的位置。


FWIW,您可以考虑使用 .follow 中的 userTrackingMode,这样就不需要所有手动位置管理器和 currentLocation 的东西。我要提到的一个警告(因为我有一天花了几个小时试图诊断这种奇怪的行为),如果你用以下方式初始化地图视图,userTrackingMode 将不起作用:

let mapView = MKMapView()

但如果你给它一些框架,它就会起作用,例如:

let mapView = MKMapView(frame: UIScreen.main.bounds)

所以,对于用户跟踪模式:

struct MapView: UIViewRepresentable {
    @Binding var userTrackingMode: MKUserTrackingMode

    func makeUIView(context: Context) -> MKMapView {
        let mapView = MKMapView(frame: UIScreen.main.bounds)
        mapView.delegate = context.coordinator
        mapView.userTrackingMode = userTrackingMode

        return mapView
    }

    func updateUIView(_ view: MKMapView, context: Context) {
        view.userTrackingMode = userTrackingMode
    }

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

    class Coordinator: NSObject, MKMapViewDelegate {
        var parent: MapView

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

        // MARK: - MKMapViewDelegate

        func mapView(_ mapView: MKMapView, didChange mode: MKUserTrackingMode, animated: Bool) {
            DispatchQueue.main.async {
                self.parent.$userTrackingMode.wrappedValue = mode
            }
        }

        // note, implementation of `mapView(_:viewFor:)` is generally not needed if we register annotation view class
    }
}

然后,我们可以在关闭用户跟踪时出现一个“关注”按钮(这样您就可以重新打开它):

struct ContentView: View {
    @State var userTrackingMode: MKUserTrackingMode = .follow

    private var locationManager = CLLocationManager()

    var body: some View {
        ZStack {
            MapView(userTrackingMode: $userTrackingMode)
                .edgesIgnoringSafeArea(.all)

            VStack {
                HStack {
                    Spacer()

                    if self.userTrackingMode == .none {
                        Button(action: {
                            self.userTrackingMode = .follow
                        }) {
                            Text("Follow")
                        }.padding()
                    }
                }

                Spacer()
            }
        }.onAppear { self.requestAuthorization() }
    }

    func requestAuthorization() {
        if CLLocationManager.authorizationStatus() == .notDetermined {
            locationManager.requestWhenInUseAuthorization()
        }
    }
}

【讨论】:

  • 谢谢,但它的工作方式与我无法用手指缩放和移动的方式相同,当我评论 view.setCenter(currentLocation, animated: true) 再次工作时,手指缩放移动.
  • @MisaelLanderos - 是的。 userTrackingMode 让您摆脱这个难题。跟随时,它将以用户为中心(并跟随),但是一旦您拖动,它将自动停止跟随。我们可以有一个“关注”按钮,仅在不关注时才会出现,将其重新打开。我已经用MCVE 更新了我的答案。
猜你喜欢
  • 2023-03-28
  • 1970-01-01
  • 2018-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多