[英]iOS - SwiftUI update text when location changes
我正在使用 SwiftUI 和 CLLocationManager。 这是我的位置模型:
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 函数完成时将更新的变量传递到视图中。
你让事情变得比他们需要的更复杂。 您无需在 model 中显式发布更改; 属性标记为@Published
,因此更改它们将自动触发属性更改。
您没有在视图中看到更新的原因是您尝试使用计算属性来访问您的 model; 这行不通。 没有任何东西可以消耗你的 model 属性的已发布更改,也没有任何东西可以告诉视图它应该刷新。
如果您只是直接在您的Text
视图中访问视图 model 属性,它将按照您想要的方式工作。
您的最终问题与反向地理编码有关。 首先,反向地理编码请求异步完成。 这意味着你不能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)")
}
}
}
反向地理编码的最后一个问题是它受到速率限制。 在开始出现错误之前,您只能在一段时间内调用它多次。 位置更新大约每秒到达一次,即使您没有移动也是如此。 大多数情况下,您将不必要地查找相同或几乎相同的位置。
一种方法是检查自上次反向位置查找以来经过的距离,并且仅在超过某个阈值(例如 500m)时才执行新查找
(我们也可以使用getTown
更聪明一些 - 将位置拆分为纬度/经度只是为了在getTown
中创建一个CLLocation
是没有意义的)
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
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.