无法从用户位置 SwiftUi 更新 CLLocation()
Posted
技术标签:
【中文标题】无法从用户位置 SwiftUi 更新 CLLocation()【英文标题】:Cant Update CLLocation() from user Location SwiftUi 【发布时间】:2021-11-04 20:16:36 【问题描述】:你好,我真的是 ios 新手,SwiftUi
所以如果我的格式有误请不要恨我哈哈。我无法将用户位置坐标转换为美国地址。我已经完成了大部分代码,但出于某种原因在线 109
我创建了 @Published var location
并将其设置到某个随机位置,然后我尝试在线更新它 l72
但由于某种原因它只使用我在109
线上传递的第一个位置。我喜欢在用户授予权限后更新和使用用户当前位置。感谢您的任何建议:)
//
// ContentView.swift
// Parked
//
// Created by Luke Jamison on 11/2/21.
//
import SwiftUI
import MapKit
import CoreLocation
import CoreLocationUI
struct ContentView: View
static let sharedV = ContentView()
@StateObject private var viewModel = ContentViewModel()
@State var addressData: String = ""
@State private var showingImagePicker = false
@State private var image: Image?
@State private var inputImage: UIImage?
//@State var geo: Any = locationManager.location
var body: some View
NavigationView
VStack
Map(coordinateRegion: $viewModel.region, showsUserLocation: true)
.onAppear
viewModel.checkIFLocationServicesisEnabled()
setData()
//viewModel.runSetDatafunc()
.frame(maxWidth: .infinity, maxHeight: .infinity).cornerRadius(20).padding()
Form
Section(header: Text("Add Location Details"))
Text(addressData)
Menu
Button(action:
self.showingImagePicker = true
)
Label("Take Picture", systemImage: "camera.shutter.button")
image?.resizable()
.frame(width: 250, height: 250)
.clipShape(Circle())
.overlay(Circle().stroke(Color.white, lineWidth: 4))
.shadow(radius: 10)
label:
Label("Add Image", systemImage: "photo.on.rectangle.angled")
if image != nil
Section(header: Text("Image Taken"))
ZStack
Rectangle().fill(Color.secondary)
if image != nil
image?.resizable().scaledToFit()
else
Button(action:
image = nil
)
Label("Remove Picture", systemImage: "trash.fill")
else
.sheet(isPresented: $showingImagePicker, onDismiss: loadImage)
ImagePicker(image: self.$inputImage)
.navigationBarTitle("Parked", displayMode: .automatic)
.navigationViewStyle(.stack)
func loadImage()
guard let inputImage = inputImage else return
image = Image(uiImage: inputImage)
func setData()
ContentViewModel.shared.resolveLoactionName(with: viewModel.location) [self] locationName in
self.addressData = locationName
struct ContentView_Previews: PreviewProvider
static var previews: some View
ContentView()
final class ContentViewModel: NSObject, ObservableObject, CLLocationManagerDelegate
static let shared = ContentViewModel()
@Published var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 33.4, longitude: -117.4), span:
MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
@Published var address = ""
@Published var location = CLLocation.init(latitude: 33, longitude: -117)
var locationManager: CLLocationManager?
func checkIFLocationServicesisEnabled()
if CLLocationManager.locationServicesEnabled()
locationManager = CLLocationManager()
locationManager!.delegate = self
//locationManager?.desiredAccuracy = kCLLocationAccuracyBest
else
print("this is off turn in the settings...")
public func resolveLoactionName(with location: CLLocation, completion: @escaping ((String) -> Void))
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(location, preferredLocale: .current) placemarks, error in
guard let place = placemarks?.first, error == nil else
completion("")
return
print(place)
var name = ""
if let locality = place.subThoroughfare
name += locality
if let street = place.thoroughfare
name += " \(street)"
if let city = place.locality
name += " \(city)"
if let adminRegion = place.administrativeArea
name += ", \(adminRegion)"
if let zipCode = place.postalCode
name += " \(zipCode)"
completion(name)
private func checkLocationAuth()
guard let locationManager = locationManager else return
switch locationManager.authorizationStatus
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
case .restricted:
print("location is restricted")
case .denied:
print("location is denied in settings for app")
case .authorizedAlways, .authorizedWhenInUse:
region = MKCoordinateRegion(center: locationManager.location!.coordinate, span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
ContentView.sharedV.setData()
self.location = CLLocation(latitude: region.self.center.latitude, longitude: region.self.center.longitude)
print(location)
//runSetDatafunc()
//address = "\(locationManager.location!.coordinate.latitude), \(locationManager.location!.coordinate.longitude)"
/*geocode(latitude: region.center.latitude, longitude: region.center.longitude) placemark, error in
let placemark = placemark?.first
//print(placemark?.description)
//self.address = "\(placemark?.thoroughfare), \(placemark?.locality), \(placemark?.administrativeArea)"
*/
@unknown default:
break
public func runSetDatafunc()
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager)
checkLocationAuth()
【问题讨论】:
你在这里有点过分了;您有一个共享的“单例”视图模型实例;不要那样做。您有一个共享的视图实例。绝对不要那样做; SwiftUI 视图是结构和临时的。每次视图更新时都会重新创建它们。您正在请求“使用时”但检查“始终”位置身份验证。获取位置是一个异步操作;所以你需要请求位置或调用startUpdatingLocation
并在委托方法中处理位置更新。
如果您致电startUpdatingLocation
,您将每秒钟左右获得一次更新。以该速率发送反向地理编码请求将导致失败,因为此服务受速率限制。
【参考方案1】:
不幸的是,由于 SwiftUI 视图的瞬态特性,您绝对不会有运气从 View
中创建单例。但是,您不需要它,因为您已经发布了您的 region
和 location
,您的 View
将根据这些信息进行更新。
这是您的代码的简化/重构版本:
struct ContentView: View
@StateObject private var viewModel = ContentViewModel()
var body: some View
VStack
Text(viewModel.address)
Map(coordinateRegion: $viewModel.region, showsUserLocation: true)
.onAppear
viewModel.checkIfLocationServicesisEnabled()
final class ContentViewModel: NSObject, ObservableObject, CLLocationManagerDelegate
static let shared = ContentViewModel()
@Published var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 33.4, longitude: -117.4), span:
MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
@Published var address = ""
@Published var location = CLLocation.init(latitude: 33, longitude: -117)
private var lastResolved : CLLocation = .init()
var locationManager: CLLocationManager?
func checkIfLocationServicesisEnabled()
if CLLocationManager.locationServicesEnabled()
locationManager = CLLocationManager()
locationManager?.startUpdatingLocation()
locationManager?.delegate = self
//locationManager?.desiredAccuracy = kCLLocationAccuracyBest
else
print("this is off turn in the settings...")
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
guard let last = locations.last else return
if last.distance(from: lastResolved) > 200
resolveLocationName(with: last) address in
self.address = address
self.lastResolved = last
self.location = last
public func resolveLocationName(with location: CLLocation, completion: @escaping ((String) -> Void))
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(location, preferredLocale: .current) placemarks, error in
guard let place = placemarks?.first, error == nil else
completion("")
return
print(place)
var name = ""
if let locality = place.subThoroughfare
name += locality
if let street = place.thoroughfare
name += " \(street)"
if let city = place.locality
name += " \(city)"
if let adminRegion = place.administrativeArea
name += ", \(adminRegion)"
if let zipCode = place.postalCode
name += " \(zipCode)"
completion(name)
private func checkLocationAuth()
guard let locationManager = locationManager else return
switch locationManager.authorizationStatus
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
case .restricted:
print("location is restricted")
case .denied:
print("location is denied in settings for app")
case .authorizedAlways, .authorizedWhenInUse:
region = MKCoordinateRegion(center: locationManager.location!.coordinate, span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
self.location = CLLocation(latitude: region.self.center.latitude, longitude: region.self.center.longitude)
print(location)
@unknown default:
break
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager)
checkLocationAuth()
请注意,我正在调用 startUpdatingLocation()
并实现 didUpdateLocations
- 这些将允许您获取/更新用户的位置。
在模拟器中,您可以使用Features->Location菜单来模拟不同的点。您会看到它解析不同的位置并在屏幕顶部显示地址。
【讨论】:
在不注意合理移动的情况下从didUpdateLocations
调用reverseGeocodeLocation
是个坏主意;您将超过速率限制
好点——使用位置约束更新,并将在答案中向 OP 添加注释。
@jnpdx 非常感谢!我看到的唯一问题是Cannot find 'lastResolved' in scope
@Luke 抱歉——我更新了一部分而不是另一部分,忘记添加属性。查看更新版本。
@Luke 如果这回答了问题,您可以使用绿色复选标记将其标记为正确吗?以上是关于无法从用户位置 SwiftUi 更新 CLLocation()的主要内容,如果未能解决你的问题,请参考以下文章
EXC_BAD_INSTRUCTION 在 Swift 中为 locationManager 崩溃(经理:CLLocationManager,didUpdateLocations 位置:[CLLoca