在后台刷新地理围栏区域的最佳方法是什么?
目前我总是使用后台模式的定位服务。我尝试抓取userlocation
并比较与初始保存位置的距离。当用户移动的距离大于我的一半mapSpan
时,应用程序将清除所有monitoredRegions
内容,firebase fetch
为搜索词生成 a 并使用搜索词执行 a MKlocalSearchRequest
。
所有找到MKMapItems
的表单MKlocalSearchRequest
都应该设置为 new GeofenceRegion
。
似乎其中的background refresh
无法monitoredRegions
正常工作。
当我启动应用程序时,附近的所有初始都monitoredRegions
被触发。但是当我开车更远的距离时,background refresh
fornew regions
似乎没有更新。
我是否需要使用不同的后台操作来使其按我的意愿工作?
以下是一些代码细节: 在MKMapViewDelegate didUpdate userLocation
我寻找我需要更新的新地理围栏区域。
func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
lbl_DebugMonitoredRegions.text = "Current monitored regions: \(locationManager.monitoredRegions.count)"
self.userLocation = userLocation.coordinate
mapView.centerCoordinate = userLocation.coordinate
let region = MKCoordinateRegionMakeWithDistance(userLocation.coordinate, CLLocationDistance(exactly: mapSpan)!, CLLocationDistance(exactly: mapSpan)!)
mapView.setRegion(region, animated: false)
if UserDefaults.standard.bool(forKey: eUserDefaultKey.isInitialLocationUpdate.rawValue){
//Update initial User Position
UpdateLastUserLocationFromUserDefaults(coordinate: userLocation.coordinate)
// Stop monitoring old regions
self.StopMonitoringForOldRegions()
//Remove old Geofence Overlays
self.RemoveOldGeofenceOverlays()
//Search nearby Shops
PerformLocalShopSearch()
}
else if UserDefaults.standard.bool(forKey: eUserDefaultKey.NeedToUpdateGeofence.rawValue){
// Stop monitoring old regions
self.StopMonitoringForOldRegions()
//Remove old Geofence Overlays
self.RemoveOldGeofenceOverlays()
//Search nearby Shops
PerformLocalShopSearch()
}
else if HasUserMovedDistanceGreaterMapSpan(userLocation: userLocation){
// Stop monitoring old regions
self.StopMonitoringForOldRegions()
//Remove old Geofence Overlays
self.RemoveOldGeofenceOverlays()
//Search nearby Shops
PerformLocalShopSearch()
}
}
在这里我检查到保存的初始位置的距离
//HasUserMovedDistanceGreaterMapSpan
private func HasUserMovedDistanceGreaterMapSpan(userLocation:MKUserLocation) -> Bool{
if let lastUserLocation = ReadLastUserLocationFromUserDefaults(){
let distance = userLocation.location?.distance(from: lastUserLocation)
if distance != nil && distance! > mapSpan * 0.25{
lbl_DebugHasUserMovedDistance.text = "User moved > mapSpan: \(debugcounter += 1)"
UpdateLastUserLocationFromUserDefaults(coordinate: userLocation.coordinate)
return true
}
}
return false
}
private func ReadLastUserLocationFromUserDefaults() -> CLLocation?{
let latitude = UserDefaults.standard.double(forKey: eUserDefaultKey.LastUserLatitude.rawValue)
let longitude = UserDefaults.standard.double(forKey: eUserDefaultKey.LastUserLongitude.rawValue)
if latitude > 0 && longitude > 0{
return CLLocation(latitude: CLLocationDegrees(floatLiteral: latitude), longitude: CLLocationDegrees(floatLiteral: longitude))
}
else { return nil }
}
private func UpdateLastUserLocationFromUserDefaults(coordinate: CLLocationCoordinate2D) -> Void{
//Set hasUserChangedGeofenceRadius false
UserDefaults.standard.set(false, forKey: eUserDefaultKey.NeedToUpdateGeofence.rawValue)
//Set isInitialLocationUpdate false
UserDefaults.standard.set(false, forKey: eUserDefaultKey.isInitialLocationUpdate.rawValue)
//SaveNew position
UserDefaults.standard.set(coordinate.latitude, forKey: eUserDefaultKey.LastUserLatitude.rawValue)
UserDefaults.standard.set(coordinate.longitude, forKey: eUserDefaultKey.LastUserLongitude.rawValue)
}
RemoveOldGeofenceOverlays 和 StopMonitoringForOldRegions
private func RemoveOldGeofenceOverlays() -> Void{
for overlay in self.MapView.overlays{
if overlay is MKUserLocation{ }
else { MapView.remove(overlay)}
}
}
func StopMonitoringForOldRegions(){
for region in locationManager.monitoredRegions {
locationManager.stopMonitoring(for: region)
NSLog("removing Region: " + region.identifier)
NSLog("Monitored regions \(self.locationManager.monitoredRegions.count)")
}
}
我的 PerformLocalShopSearch
private func PerformLocalShopSearch() -> Void{
let rad = UserDefaults.standard.double(forKey: eUserDefaultKey.MonitoredRadius.rawValue)
radiusToMonitore = CLLocationDistance(exactly: rad)
if radiusToMonitore == 0 { return }
firebaseShoppingList.GetStoresForGeofencing()
}
从 Firebase 收到商店时我的代表
func StoresCollectionReceived() {
for store in StoresArray {
let request = MKLocalSearchRequest()
request.naturalLanguageQuery = store
request.region = MapView.region
let search = MKLocalSearch(request: request)
search.start { (response, error) in
if error != nil {
print(error!.localizedDescription)
return
}
if response!.mapItems.count == 0 {
NSLog("No local search matches found for \(store)")
return
}
NSLog("Matches found for \(store)")
self.StartMonitoringGeofenceRegions(mapItems: response!.mapItems)
}
}
}
至少我开始监控圆形区域的功能
internal func StartMonitoringGeofenceRegions(mapItems: [MKMapItem]){
if self.userLocation == nil { return }
var possibleRegionsPerStore = Int(round(Double(StoresArray.count / 20)))
possibleRegionsPerStore = possibleRegionsPerStore < 4 ? 4: possibleRegionsPerStore
var itemsCount = 0
itemsCount = TryMonitoreRegion(mapItems: mapItems, possibleRegionsPerStore: possibleRegionsPerStore, itemsCount: itemsCount, minDistance: 0, maxDistance: mapSpan * 0.1)
if itemsCount == possibleRegionsPerStore { return }
itemsCount = TryMonitoreRegion(mapItems: mapItems, possibleRegionsPerStore: possibleRegionsPerStore, itemsCount: itemsCount, minDistance: mapSpan * 0.1, maxDistance: mapSpan * 0.2)
if itemsCount == possibleRegionsPerStore { return }
itemsCount = TryMonitoreRegion(mapItems: mapItems, possibleRegionsPerStore: possibleRegionsPerStore, itemsCount: itemsCount, minDistance: mapSpan * 0.2, maxDistance: mapSpan * 0.3)
if itemsCount == possibleRegionsPerStore { return }
itemsCount = TryMonitoreRegion(mapItems: mapItems, possibleRegionsPerStore: possibleRegionsPerStore, itemsCount: itemsCount, minDistance: mapSpan * 0.3, maxDistance: mapSpan * 0.4)
if itemsCount == possibleRegionsPerStore { return }
itemsCount = TryMonitoreRegion(mapItems: mapItems, possibleRegionsPerStore: possibleRegionsPerStore, itemsCount: itemsCount, minDistance: mapSpan * 0.4, maxDistance: mapSpan * 0.6)
if itemsCount == possibleRegionsPerStore { return }
itemsCount = TryMonitoreRegion(mapItems: mapItems, possibleRegionsPerStore: possibleRegionsPerStore, itemsCount: itemsCount, minDistance: mapSpan * 0.6, maxDistance: mapSpan * 3)
}
private func TryMonitoreRegion(mapItems:[MKMapItem], possibleRegionsPerStore:Int, itemsCount:Int, minDistance:Double, maxDistance:Double) -> Int{
for mapItem:MKMapItem in mapItems{
self.SetAnnotations(mapItem: mapItem)
if itemsCount == possibleRegionsPerStore { return itemsCount }
let distanceToUser = CalculateDistanceBetweenTwoCoordinates(location1: userLocation!, location2: mapItem.placemark.coordinate)
//Monitore 6th nearest stores if regions count still below 20
if locationManager.monitoredRegions.count < 20 && distanceToUser >= minDistance && distanceToUser <= maxDistance {
MonitoreCircularRegion(mapItem: mapItem)
return itemsCount + 1
}
}
return itemsCount
}
private func MonitoreCircularRegion(mapItem: MKMapItem){
DispatchQueue.main.async {
let region = CLCircularRegion(center: mapItem.placemark.coordinate, radius: CLLocationDistance(self.radiusToMonitore), identifier: "\(UUID().uuidString)\("SB_")\(mapItem.name!)")
self.locationManager.startMonitoring(for: region)
NSLog("Monitored Regions: \(self.locationManager.monitoredRegions.count)")
NSLog("MapSpan: \(self.mapSpan)")
NSLog("Start monitoring for Region: \(region) with Radius \(region.radius)" )
}
}
当我移出地图跨度时,我仍然没有在后台获得更新。我还观察到的是,在我的 MKLocalSearchRequest 中,我得到了两次循环的四次响应,每个请求似乎都提供了两个响应。那是对的吗?