You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS应用被杀时进入监控区域触发本地通知实现咨询

iOS 地理围栏本地通知实现(支持应用被杀状态触发)

需求说明

  • 用户到达本地存储的附近目的地时,发送iOS本地通知
  • 在CLLocationManager代理的didEnterRegion方法中触发通知
  • 应用被杀死状态下仍能触发通知
  • 应用被杀时,监控用户最后位置附近的20个区域
  • 用户进入监控区域时自动触发本地通知

核心实现代码

首先定义必要的属性与初始化配置:

import CoreLocation
import UserNotifications

class LocationMonitor: NSObject, CLLocationManagerDelegate, UNUserNotificationCenterDelegate {
    private let locationManager = CLLocationManager()
    private var regions: [CLCircularRegion] = []
    private var monitoredRegions: Set<CLCircularRegion> = []
    private var lastLocation: CLLocation?
    
    override init() {
        super.init()
        setupLocationManager()
        setupNotificationPermission()
    }
    
    private func setupLocationManager() {
        locationManager.delegate = self
        // 申请Always权限,后台/被杀状态监控必须用该权限
        locationManager.requestAlwaysAuthorization()
        locationManager.allowsBackgroundLocationUpdates = true
        locationManager.pausesLocationUpdatesAutomatically = false
    }
    
    private func setupNotificationPermission() {
        UNUserNotificationCenter.current().delegate = self
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { granted, error in
            if let error = error {
                print("通知权限申请失败:\(error.localizedDescription)")
            }
        }
    }

生成用户当前位置周边的随机区域:

func generateSurroundingRegions(currentLocation: CLLocation) {
        regions.removeAll()

        for i in 1...100 {
            // 生成随机角度(0-360度)和距离(20-1020米)
            let angle = Double(arc4random_uniform(360)) * Double.pi / 180
            let distance = Double(arc4random_uniform(1000) + 20)

            // 计算区域中心坐标
            let newLatitude = currentLocation.coordinate.latitude + (distance / 111000) * cos(angle)
            let newLongitude = currentLocation.coordinate.longitude + (distance / (111000 * cos(currentLocation.coordinate.latitude * Double.pi / 180))) * sin(angle)

            let region = CLCircularRegion(center: CLLocationCoordinate2D(latitude: newLatitude, longitude: newLongitude), radius: 50, identifier: "Region\(i)")
            regions.append(region)
        }
    }

更新监控区域(保留最近的20个,符合iOS系统限制):

func updateMonitoredRegions(currentLocation: CLLocation) {
        // 按距离当前位置由近到远排序
        let sortedRegions = regions.sorted {
            let location1 = CLLocation(latitude: $0.center.latitude, longitude: $0.center.longitude)
            let location2 = CLLocation(latitude: $1.center.latitude, longitude: $1.center.longitude)
            return currentLocation.distance(from: location1) < currentLocation.distance(from: location2)
        }

        let closestRegions = Array(sortedRegions.prefix(20))
        let newMonitoredRegions = Set(closestRegions)

        // 停止监控不需要的区域
        for region in monitoredRegions.subtracting(newMonitoredRegions) {
            locationManager.stopMonitoring(for: region)
        }

        // 启动新区域的监控
        for region in newMonitoredRegions.subtracting(monitoredRegions) {
            region.notifyOnEntry = true
            region.notifyOnExit = false // 仅监听进入事件,可按需调整
            locationManager.startMonitoring(for: region)
        }

        monitoredRegions = newMonitoredRegions
    }

位置更新时同步监控区域:

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let currentLocation = locations.last else { return }
        lastLocation = currentLocation
        generateSurroundingRegions(currentLocation: currentLocation)
        updateMonitoredRegions(currentLocation: currentLocation)
    }

进入区域时触发通知:

func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
        sendLocalNotification(regionID: region.identifier)
        print("进入区域:\(region.identifier)")
    }
    
    private func sendLocalNotification(regionID: String) {
        let content = UNMutableNotificationContent()
        content.title = "到达目标区域"
        content.body = "你已进入区域:\(regionID)"
        content.sound = .default

        let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
        UNUserNotificationCenter.current().add(request) { error in
            if let error = error {
                print("通知发送失败:\(error.localizedDescription)")
            }
        }
    }
}

关键注意事项

  • 权限配置:必须申请Always位置权限,WhenInUse权限无法支持应用被杀状态的监控。需在Info.plist中添加NSLocationAlwaysAndWhenInUseUsageDescription和NSLocationWhenInUseUsageDescription字段说明权限用途。
  • 后台模式:在Xcode项目的Signing & Capabilities中开启Background Modes,勾选Location updates选项。
  • 系统限制:iOS单应用最多支持20个地理围栏监控,代码中取最近20个区域符合该限制。
  • 被杀状态触发逻辑:应用被杀后,系统会接管地理围栏监控,用户进入区域时系统会唤醒应用(后台状态)并调用didEnterRegion方法,从而触发通知。
  • 坐标精度:代码中使用的坐标转换为近似计算,若需更高精度可借助CoreLocation的专业坐标计算API。

内容的提问来源于stack exchange,提问作者Jay Pratap singh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 03:04:55