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

如何用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 09:00:13