[英]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: CLLocationCoordinate2D
和view.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.