iOS區(qū)域監(jiān)控(地理圍欄)
區(qū)域監(jiān)控,高德地圖上叫地理圍欄,兩者都是同一個意思。此功能實現(xiàn)的是:首先創(chuàng)建一個區(qū)域(圍欄),當用戶設(shè)備進入或者離開此區(qū)域時,會有相應(yīng)的代理方法響應(yīng),開發(fā)者可以做一些操作。并且最重要的一點是當開啟了區(qū)域監(jiān)控,即使用戶殺死了APP還是可以監(jiān)聽到代理方法的響應(yīng),從而做一些操作。

地理圍欄.jpg
位置權(quán)限:必須是始終運行訪問地理位置權(quán)限,這樣在殺死狀態(tài)下才能通過區(qū)域監(jiān)控喚醒APP獲取位置信息。
開始我接入的是高德SDK,但不知是何原因?qū)е挛覛⑺繟PP時地理圍欄并沒有喚醒APP。所以我換成了系統(tǒng)CoreLocation框架實現(xiàn)此功能。
一、導入框架
import CoreLocation
二、初始化CLLocationManager
locationManager = CLLocationManager()
locationManager.delegate = self
//必須滿足始終允許地理位置訪問
locationManager.requestAlwaysAuthorization()
locationManager.pausesLocationUpdatesAutomatically = false
if #available(iOS 9.0, *) {
locationManager.allowsBackgroundLocationUpdates = true
} else {
// Fallback on earlier versions
}
三、遵從代理CLLocationManagerDelegate
// MARK: - 區(qū)域監(jiān)控代理方法
extension AppLocationManager {
/// 進入?yún)^(qū)域
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
debugPrint("進入?yún)^(qū)域")
}
/// 離開區(qū)域
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
debugPrint("離開區(qū)域")
}
/// 請求某個區(qū)域的狀態(tài)
func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
// CLRegionStateUnknown, 未知狀態(tài)
// CLRegionStateInside, // 在區(qū)域內(nèi)部
// CLRegionStateOutside // 區(qū)域外面
var msg = ""
if(state == .inside)
{
debugPrint("在區(qū)域內(nèi)")
msg = "在區(qū)域內(nèi)"
}else if (state == .outside)
{
debugPrint("在區(qū)域外")
msg = "在區(qū)域外"
} else {
debugPrint("未知區(qū)域")
msg = "未知區(qū)域"
}
//發(fā)送本地推送
LocalNotificationManager.addNotification(msg: msg)
}
/// 監(jiān)聽區(qū)域失敗
func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error) {
// 經(jīng)驗: 一般在這里, 做移除最遠的區(qū)域
// [manager stopMonitoringForRegion:最遠區(qū)域]
debugPrint(error)
}
}
四、區(qū)域定位喚醒被殺死APP
當用戶設(shè)置始終允許訪問地理位置權(quán)限時,即使APP被殺死了,當進入?yún)^(qū)域或者離開區(qū)域時都能喚醒APP,這時我們就可以在AppDelegate方法中func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool判斷是否是區(qū)域定位喚醒的從而做一些操作。
/// APP被喚醒機制
func awakeForRegion(launchOptions: [UIApplication.LaunchOptionsKey: Any]?) {
let flag = launchOptions?.keys.contains(UIApplication.LaunchOptionsKey.location) ?? false
if flag {
LocalNotificationManager.addNotification(msg: "被區(qū)域定位喚醒")
}
let isNotification = launchOptions?.keys.contains(UIApplication.LaunchOptionsKey.remoteNotification) ?? false
if isNotification {
LocalNotificationManager.addNotification(msg: "被通知推送喚醒")
}
}
五、開啟區(qū)域監(jiān)控
做好了以上步驟就可以實現(xiàn)區(qū)域監(jiān)控代碼了,區(qū)域監(jiān)控顧名思義就是要劃定一個區(qū)域,系統(tǒng)給我們提供了好幾種劃定區(qū)域的方法,我們選擇最簡單的一種--畫圓。
/// 開啟區(qū)域定位
func regionWatch() {
if CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
guard let lat = AppLoginUserManager.default.dormLat, let lng = AppLoginUserManager.default.dormLng else {
return
}
guard lat > 0 && lng > 0 else { return }
var center = CLLocationCoordinate2DMake(lat, lng)
// 中國國測局地理坐標(GCJ-02) 轉(zhuǎn)換成 世界標準地理坐標(WGS-84)
// 這里是因為我們使用的是國內(nèi)的坐標系統(tǒng),但是iOS系統(tǒng)獲取的是世界標注坐標系統(tǒng)所以我們需要轉(zhuǎn)換一下
// 如果你們后端提供的坐標也是世界標準的話就不需要轉(zhuǎn)化了
// JZLocationConverter是一個第三方庫:https://github.com/JackZhouCn/JZLocationConverter
center = JZLocationConverter.gcj02(toWgs84: center)
var radius: Double = 20
if let r = AppLoginUserManager.default.locationRadius, r > 0 {
radius = r
}
//判斷半徑是否超過系統(tǒng)的最大
if radius > self.locationManager.maximumRegionMonitoringDistance {
radius = self.locationManager.maximumRegionMonitoringDistance
}
let region = CLCircularRegion(center: center, radius: radius, identifier: "NZ")
//監(jiān)聽進入?yún)^(qū)域
region.notifyOnEntry = true
//監(jiān)聽離開區(qū)域
region.notifyOnExit = true
//開始區(qū)域監(jiān)聽
locationManager.startMonitoring(for: region)
locationManager.requestState(for: region)
}
}