1

在后台刷新地理围栏区域的最佳方法是什么?

目前我总是使用后台模式的定位服务。我尝试抓取userlocation并比较与初始保存位置的距离。当用户移动的距离大于我的一半mapSpan时,应用程序将清除所有monitoredRegions内容,firebase fetch为搜索词生成 a 并使用搜索词执行 a MKlocalSearchRequest

所有找到MKMapItems的表单MKlocalSearchRequest都应该设置为 new GeofenceRegion

似乎其中的background refresh无法monitoredRegions正常工作。

当我启动应用程序时,附近的所有初始都monitoredRegions被触发。但是当我开车更远的距离时,background refreshfornew 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 中,我得到了两次循环的四次响应,每个请求似乎都提供了两个响应。那是对的吗?

4

0 回答 0