如何用Swift子类化UNNotificationTrigger实现SwiftUI海拔触发iOS通知?
实现海拔触发通知的正确方式
首先明确:UNNotificationTrigger不能被子类化,苹果的UserNotifications框架不允许自定义触发器子类,所以得换个思路——用Core Location持续监听海拔变化,当达到2000米阈值时主动触发本地通知。
实现步骤
1. 配置权限与后台模式
- 在
Info.plist中添加两个权限描述:NSLocationAlwaysAndWhenInUseUsageDescription:说明后台监听海拔的必要性(比如"需要在后台监听海拔变化以发送登山达标通知")NSLocationWhenInUseUsageDescription:前台使用位置的描述
- 在Xcode项目的
Signing & Capabilities里,添加Background Modes并勾选Location updates,确保后台能持续获取海拔数据。
2. 核心逻辑实现
创建一个管理位置和通知的ObservableObject,处理权限请求、海拔监听和通知发送:
import SwiftUI import CoreLocation import UserNotifications class AltitudeNotificationManager: NSObject, ObservableObject, CLLocationManagerDelegate { private let locationManager = CLLocationManager() private let targetAltitude: CLLocationDistance = 2000 // 目标海拔(米) private var hasTriggeredNotification = false // 避免重复触发 override init() { super.init() locationManager.delegate = self locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation locationManager.activityType = .fitness // 适配登山运动场景 locationManager.allowsBackgroundLocationUpdates = true locationManager.pausesLocationUpdatesAutomatically = false } // 请求通知和位置权限 func requestPermissions() { UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { _, _ in } locationManager.requestAlwaysAuthorization() } // 启动海拔监听 func startMonitoring() { if CLLocationManager.locationServicesEnabled() { locationManager.startUpdatingLocation() } } // 位置更新回调,判断海拔是否达标 func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { guard let latestLocation = locations.last else { return } // 加±10米缓冲,抵消海拔数据误差,避免频繁触发 let lowerBound = targetAltitude - 10 let upperBound = targetAltitude + 10 if latestLocation.altitude >= lowerBound && latestLocation.altitude <= upperBound && !hasTriggeredNotification { sendAltitudeNotification() hasTriggeredNotification = true // 可选:触发后停止监听,减少耗电 // locationManager.stopUpdatingLocation() } } // 发送达标通知 private func sendAltitudeNotification() { let content = UNMutableNotificationContent() content.title = "海拔达标提醒" content.body = "你已经到达2000米海拔!" content.sound = .default let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false) let request = UNNotificationRequest(identifier: "AltitudeNotification", content: content, trigger: trigger) UNUserNotificationCenter.current().add(request) { error in if let error = error { print("通知发送失败:\(error.localizedDescription)") } } } // 权限状态变化回调 func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { switch manager.authorizationStatus { case .authorizedAlways, .authorizedWhenInUse: startMonitoring() default: // 权限未授权,可提示用户前往设置开启 break } } }
3. 在SwiftUI View中集成
把管理器注入View环境,初始化时触发权限请求:
struct ContentView: View { @StateObject private var altitudeManager = AltitudeNotificationManager() var body: some View { VStack { Text("正在监听海拔变化...") .padding() } .onAppear { altitudeManager.requestPermissions() } } }
注意事项
- 海拔数据存在一定误差,设置缓冲区间能有效避免误触发
- 后台持续监听会增加设备耗电,建议给用户提供手动开启/关闭监听的开关
- 测试时可使用Xcode的位置模拟功能,自定义海拔值验证触发逻辑
内容的提问来源于stack exchange,提问作者Stone Lai
相关产品推荐
相关产品推荐

