简体   繁体   English

使用 SwiftUI 上的当前位置更新 MapView

[英]Update MapView with current location on SwiftUI

Trying to update the mapview of the Project 14 of 100daysOfSwiftUI to show my current location, the problem i can´t zoom in move around尝试更新 100daysOfSwiftUI 项目 14 的地图视图以显示我当前的位置,问题我无法放大移动

i have this code i add @Binding var currentLocation: CLLocationCoordinate2D and view.setCenter(currentLocation, animated: true) to my MapView so i have a button that send thats value and the view actually move so slow to the location but then i can move away anymore我有这段代码我将@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

     }

    }
}

an this is my swiftUI view这是我的 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

    }
    ...

UPDATE更新

thanks for the support I using this class to call locationManager.requestWhenInUseAuthorization()感谢您的支持,我使用此 class 调用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)
    }

}

i just want to center my mapview on my current location when i press the button当我按下按钮时,我只想将我的地图视图集中在我当前的位置

No where here do you ever call locationManager.requestWhenInUseAuthorization() .没有你曾经在这里调用locationManager.requestWhenInUseAuthorization() When I did that (of course, making sure the Info.plist had an entry for NSLocationWhenInUseUsageDescription ), it updated the location correctly.当我这样做时(当然,确保Info.plist有一个NSLocationWhenInUseUsageDescription条目),它正确更新了位置。

Eg例如

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

Now, this is just a quick and dirty fix to demonstrate that it works.现在,这只是一个快速而肮脏的修复,以证明它有效。 But it's not quite right, because the first time you call getCurrentLocation , if it has to ask the user for permission, which it does asynchronously, which means that it won't yet have a location when you get to the lastLocation line in your implementation.但这并不完全正确,因为第一次调用getCurrentLocation时,如果它必须向用户请求许可,它是异步执行的,这意味着当您到达实现中的lastLocation行时它还没有位置. This is a one time thing, but still, it's not acceptable.这是一次性的事情,但仍然不能接受。 You'd want your CLLocationManagerDelegate update currentLocation if needed.如果需要,您希望您的CLLocationManagerDelegate更新currentLocation But hopefully you've got enough here to diagnose why your location is not being captured correctly by the CLLocationManager .但希望您在这里有足够的信息来诊断为什么您的位置没有被CLLocationManager正确捕获。


FWIW, you might consider using a userTrackingMode of .follow , which obviates the need for all of this manual location manager and currentLocation stuff. FWIW,您可能会考虑使用userTrackingMode.follow ,这样就不需要所有这些手动位置管理器和currentLocation东西。 The one caveat I'll mention (because I spent hours one day trying to diagnose this curious behavior), is that the userTrackingMode doesn't work if you initialize your map view with:我要提到的一个警告(因为我一天花了几个小时试图诊断这种奇怪的行为)是,如果您使用以下命令初始化 map 视图,则userTrackingMode不起作用:

let mapView = MKMapView()

But it works if you do give it some frame, eg:但如果你给它一些框架,它就会起作用,例如:

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

So, for user tracking mode:因此,对于用户跟踪模式:

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
    }
}

And then, we can have a “follow” button that appears when user tracking is turned off (so that you can turn it back on):然后,我们可以在关闭用户跟踪时出现一个“关注”按钮(以便您可以重新打开它):

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()
        }
    }
}

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

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