如何解决iOS - 位置更改时 SwiftUI 更新文本
我正在使用 SwiftUI 和 CLLocationManager。这是我的 LocationModel:
class Locationviewmodel: NSObject,ObservableObject{
@Published var userLatitude: Double = 0
@Published var userLongitude: Double = 0
@Published var userTown: String = ""
var objectwillChange = ObservableObjectPublisher()
private let locationManager = CLLocationManager()
override init() {
super.init()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
}
extension Locationviewmodel: CLLocationManagerDelegate {
struct ReversedGeoLocation {
let name: String // eg. Apple Inc.
let streetName: String // eg. Infinite Loop
let streetNumber: String // eg. 1
let city: String // eg. Cupertino
let state: String // eg. CA
let zipCode: String // eg. 95014
let country: String // eg. United States
let isoCountryCode: String // eg. US
var formattedAddress: String {
return """
\(name),\(streetNumber) \(streetName),\(city),\(state) \(zipCode)
\(country)
"""
}
// Handle optionals as needed
init(with placemark: CLPlacemark) {
self.name = placemark.name ?? ""
self.streetName = placemark.thoroughfare ?? ""
self.streetNumber = placemark.subThoroughfare ?? ""
self.city = placemark.locality ?? ""
self.state = placemark.administrativeArea ?? ""
self.zipCode = placemark.postalCode ?? ""
self.country = placemark.country ?? ""
self.isoCountryCode = placemark.isoCountryCode ?? ""
}
}
func locationManager(_ manager: CLLocationManager,didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
userLatitude = location.coordinate.latitude
userLongitude = location.coordinate.longitude
userTown = getTown(lat: CLLocationdegrees.init(userLatitude),long: CLLocationdegrees.init(userLongitude))
print(location)
}
func getTown(lat: CLLocationdegrees,long: CLLocationdegrees) -> String
{
var town = ""
let location = CLLocation.init(latitude: lat,longitude: long)
CLGeocoder().reverseGeocodeLocation(location) { placemarks,error in
guard let placemark = placemarks?.first else {
let errorString = error?.localizedDescription ?? "Unexpected Error"
print("Unable to reverse geocode the given location. Error: \(errorString)")
return
}
let reversedGeoLocation = ReversedGeoLocation(with: placemark)
print(reversedGeoLocation.city)
town = reversedGeoLocation.city
self.objectwillChange.send()
}
return town
}
}
现在我想显示当前坐标和城市,但城市没有显示,似乎变量没有正确更新。怎么做?这是我的观点:
@Observedobject var locationviewmodel = Locationviewmodel()
var latitude: Double { return(locationviewmodel.userLatitude ) }
var longitude: Double { return(locationviewmodel.userLongitude ) }
var town: String { return(locationviewmodel.userTown) }
var body: some View {
vstack {
Text("Town: \(town)")
Text("Latitude: \(latitude)")
Text("Longitude: \(longitude)")
}
}
我不完全理解如何在位置更改或 getTown 函数完成关闭时将更新的变量传递到视图中。
解决方法
你让事情变得比他们需要的更复杂。您不需要在模型中显式发布更改;属性标记为 @Published
,因此更改它们将自动触发属性更改。
您没有在视图中看到更新的原因是因为您尝试使用计算属性来访问您的模型;这行不通。没有什么可以使用已发布的对模型属性的更改,也没有什么可以告诉视图它应该刷新。
如果您只是直接在 Text
视图中访问视图模型属性,它将按您想要的方式工作。
您最后的问题与反向地理编码有关。首先,反向地理编码请求异步完成。这意味着您不能return town
。同样,您可以直接更新 userTown
属性,将其分派到主队列,因为您不能保证会在主队列上调用反向地理编码处理程序,并且所有 UI 更新都必须在主队列上执行。
把所有这些放在一起得到
class LocationViewModel: NSObject,ObservableObject{
@Published var userLatitude: Double = 0
@Published var userLongitude: Double = 0
@Published var userTown: String = ""
private let locationManager = CLLocationManager()
override init() {
super.init()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
}
extension LocationViewModel: CLLocationManagerDelegate {
struct ReversedGeoLocation {
let name: String // eg. Apple Inc.
let streetName: String // eg. Infinite Loop
let streetNumber: String // eg. 1
let city: String // eg. Cupertino
let state: String // eg. CA
let zipCode: String // eg. 95014
let country: String // eg. United States
let isoCountryCode: String // eg. US
var formattedAddress: String {
return """
\(name),\(streetNumber) \(streetName),\(city),\(state) \(zipCode)
\(country)
"""
}
// Handle optionals as needed
init(with placemark: CLPlacemark) {
self.name = placemark.name ?? ""
self.streetName = placemark.thoroughfare ?? ""
self.streetNumber = placemark.subThoroughfare ?? ""
self.city = placemark.locality ?? ""
self.state = placemark.administrativeArea ?? ""
self.zipCode = placemark.postalCode ?? ""
self.country = placemark.country ?? ""
self.isoCountryCode = placemark.isoCountryCode ?? ""
}
}
func locationManager(_ manager: CLLocationManager,didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
userLatitude = location.coordinate.latitude
userLongitude = location.coordinate.longitude
getTown(lat: CLLocationDegrees.init(userLatitude),long: CLLocationDegrees.init(userLongitude))
print(location)
}
func getTown(lat: CLLocationDegrees,long: CLLocationDegrees) -> Void
{
let location = CLLocation.init(latitude: lat,longitude: long)
CLGeocoder().reverseGeocodeLocation(location) { placemarks,error in
guard let placemark = placemarks?.first else {
let errorString = error?.localizedDescription ?? "Unexpected Error"
print("Unable to reverse geocode the given location. Error: \(errorString)")
return
}
let reversedGeoLocation = ReversedGeoLocation(with: placemark)
print(reversedGeoLocation.city)
DispatchQueue.main.async {
self.userTown = reversedGeoLocation.city
}
}
}
}
struct ContentView: View {
@ObservedObject var locationViewModel = LocationViewModel()
var body: some View {
VStack {
Text("Town: \(locationViewModel.userTown)")
Text("Latitude: \(locationViewModel.userLatitude)")
Text("Longitude: \(locationViewModel.userLongitude)")
}
}
}
反向地理编码的最后一个问题是速率受限;在开始出现错误之前,您只能在一段时间内调用它多次。即使您没有移动,位置更新也会大约每秒到达一次。大多数情况下,您会不必要地查找相同或几乎相同的位置。
一种方法是检查自上次反向位置查找以来行进的距离,并且仅在超过某个阈值(例如 500 米)时才执行新查找
(我们也可以更聪明地使用 getTown
- 将位置拆分为纬度/经度只是为了在 CLLocation
中创建 getTown
没有意义)
private var lastTownLocation: CLLocation? = nil
func locationManager(_ manager: CLLocationManager,didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
userLatitude = location.coordinate.latitude
userLongitude = location.coordinate.longitude
if self.lastTownLocation == nil || self.lastTownLocation!.distance(from: location) > 500 {
getTown(location)
}
}
func getTown(_ location: CLLocation) -> Void
{
CLGeocoder().reverseGeocodeLocation(location) { placemarks,error in
guard let placemark = placemarks?.first else {
let errorString = error?.localizedDescription ?? "Unexpected Error"
print("Unable to reverse geocode the given location. Error: \(errorString)")
return
}
self.lastTownLocation = location
let reversedGeoLocation = ReversedGeoLocation(with: placemark)
print(reversedGeoLocation.city)
DispatchQueue.main.async {
self.userTown = reversedGeoLocation.city
}
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。