SwiftUI - App 进入前台时获取位置和调用函数
SwiftUI - Get Location and Call Function when App enters Foreground
我是 SwiftUI 的新手,我遇到了一些我认为相当简单的事情。
我创建了一个显示用户当前位置的应用程序。如果用户将应用程序置于后台,并且用户的物理位置发生变化,则应用程序在进入前台时不会显示新位置。如果我终止该应用程序,然后重新启动,它会显示新位置。
我还想调用一个函数,以便在检测到位置更改时从 Firebase 获取更新的数据集。这是我正在使用的代码的简化版本。请让我知道我错过了什么。我想我应该使用更多的 Combine 功能来实现这个结果,但我不确定该怎么做?
我的关键问题是:“我如何检测视图中的位置变化并根据该位置变化调用函数?”
MyView.swift
import SwiftUI
struct MyView: View {
@ObservedObject var locationManager = LocationManager()
var userLatitude: String {
return "\(locationManager.lastLocation?.coordinate.latitude ?? 0)"
}
var userLongitude: String {
return "\(locationManager.lastLocation?.coordinate.longitude ?? 0)"
}
var body: some View {
VStack {
Text("location status: \(locationManager.statusString)")
HStack {
Text("latitude: \(userLatitude)")
Text("longitude: \(userLongitude)")
}
Text("place: \(locationManager.placemark?.thoroughfare ?? "")")
}
}
func refreshData() {
// call firebase listener with new location data
}
}
LocationManager.swift
import Foundation
import CoreLocation
import Combine
class LocationManager: NSObject, ObservableObject {
let objectWillChange = PassthroughSubject<Void, Never>()
private let locationManager = CLLocationManager()
override init() {
super.init()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
@Published var locationStatus: CLAuthorizationStatus? {
willSet {
objectWillChange.send()
}
}
@Published var lastLocation: CLLocation? {
willSet {
objectWillChange.send()
}
}
@Published var placemark: CLPlacemark? {
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"
}
}
}
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)
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(location) { (placemarks, error) in
if (error != nil){
print("Error in reverseGeocode: \(error)")
return
}
let placemark = placemarks! as [CLPlacemark]
if placemark.count > 0 {
let placemark = placemarks![0]
self.placemark = placemark
}
}
locationManager.stopUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
if let error = error as? CLError, error.code == .denied {
// Location updates are not authorized.
locationManager.stopUpdatingLocation()
return
}
// Notify the user of any errors.
}
}
您可以订阅 LocationManager class 中的 lastLocation 属性,以便在发生更改时得到通知。使用 Combine,您可以像这样实现:
// inside the location manager class
private var cancellables: [AnyCancellable] = [] // new property to keep track of your subscriptions
// inside the initialiser of the class
$lastLocation
.receive(on: RunLoop.main) // main thread so view gets an update
.sink { lastLocation in // subscribe to the published property. Anytime it changes you will get notified
refreshData()
// do more with the new value of last location or not :)
}
.store(in: cancellabels) // store subscription in property so it doesn’t go out of scope immediately
此外,在您看来,请确保绕过计算属性并直接订阅已发布的位置 属性。根据我的经验,计算属性和订阅不能很好地协同工作。
在您的文本视图中直接引用位置坐标:
Text(locationManager.lastLocation?.coordinate.latitude ?? 0)
我是 SwiftUI 的新手,我遇到了一些我认为相当简单的事情。 我创建了一个显示用户当前位置的应用程序。如果用户将应用程序置于后台,并且用户的物理位置发生变化,则应用程序在进入前台时不会显示新位置。如果我终止该应用程序,然后重新启动,它会显示新位置。
我还想调用一个函数,以便在检测到位置更改时从 Firebase 获取更新的数据集。这是我正在使用的代码的简化版本。请让我知道我错过了什么。我想我应该使用更多的 Combine 功能来实现这个结果,但我不确定该怎么做?
我的关键问题是:“我如何检测视图中的位置变化并根据该位置变化调用函数?”
MyView.swift
import SwiftUI
struct MyView: View {
@ObservedObject var locationManager = LocationManager()
var userLatitude: String {
return "\(locationManager.lastLocation?.coordinate.latitude ?? 0)"
}
var userLongitude: String {
return "\(locationManager.lastLocation?.coordinate.longitude ?? 0)"
}
var body: some View {
VStack {
Text("location status: \(locationManager.statusString)")
HStack {
Text("latitude: \(userLatitude)")
Text("longitude: \(userLongitude)")
}
Text("place: \(locationManager.placemark?.thoroughfare ?? "")")
}
}
func refreshData() {
// call firebase listener with new location data
}
}
LocationManager.swift
import Foundation
import CoreLocation
import Combine
class LocationManager: NSObject, ObservableObject {
let objectWillChange = PassthroughSubject<Void, Never>()
private let locationManager = CLLocationManager()
override init() {
super.init()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
@Published var locationStatus: CLAuthorizationStatus? {
willSet {
objectWillChange.send()
}
}
@Published var lastLocation: CLLocation? {
willSet {
objectWillChange.send()
}
}
@Published var placemark: CLPlacemark? {
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"
}
}
}
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)
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(location) { (placemarks, error) in
if (error != nil){
print("Error in reverseGeocode: \(error)")
return
}
let placemark = placemarks! as [CLPlacemark]
if placemark.count > 0 {
let placemark = placemarks![0]
self.placemark = placemark
}
}
locationManager.stopUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
if let error = error as? CLError, error.code == .denied {
// Location updates are not authorized.
locationManager.stopUpdatingLocation()
return
}
// Notify the user of any errors.
}
}
您可以订阅 LocationManager class 中的 lastLocation 属性,以便在发生更改时得到通知。使用 Combine,您可以像这样实现:
// inside the location manager class
private var cancellables: [AnyCancellable] = [] // new property to keep track of your subscriptions
// inside the initialiser of the class
$lastLocation
.receive(on: RunLoop.main) // main thread so view gets an update
.sink { lastLocation in // subscribe to the published property. Anytime it changes you will get notified
refreshData()
// do more with the new value of last location or not :)
}
.store(in: cancellabels) // store subscription in property so it doesn’t go out of scope immediately
此外,在您看来,请确保绕过计算属性并直接订阅已发布的位置 属性。根据我的经验,计算属性和订阅不能很好地协同工作。 在您的文本视图中直接引用位置坐标:
Text(locationManager.lastLocation?.coordinate.latitude ?? 0)