swift - 使用 SwiftUI 上的当前位置更新 MapView
问题描述
尝试更新 100daysOfSwiftUI 项目 14 的地图视图以显示我当前的位置,问题我无法放大移动
我有这个代码,我添加@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
}
}
}
这是我的 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)
}
}
当我按下按钮时,我只想将我的地图视图集中在我当前的位置
解决方案
没有你在这里打电话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,您可能会考虑使用userTrackingMode
of .follow
,这消除了对所有这些手动位置管理器和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()
}
}
}
推荐阅读
- amazon-web-services - 应用程序负载均衡器在自动缩放组打开新实例之前不会保留请求
- javascript - React Toggle 在移动屏幕上不起作用
- python - 如何堆叠不同大小的矩阵
- react-native-ios - 无法在 iOS 中运行 react-native 应用程序,但是我将我的 xcode 升级到 12
- apache-kafka - 如何监控 kafka 主题的特定分区上的传入消息率?
- node.js - postgreSql中如何使用sinon模拟池的连接、查询、释放方法?
- nothing - PHP - 我怎样才能拥有所有 php 代码的列表?
- pine-script - 卷值标签未显示在垂直卷的值栏上
- elasticsearch - Elasticsearch 不模糊
- javascript - 如何将滑块值与另一个值相加?