Swift iOS:检测本地通知是否显示及锁屏/杀后台时获取通知数据
嘿,我来帮你搞定这两个Swift iOS开发里的本地通知问题~你提到用UNUserNotificationCenter的present和didReceive没生效,大概率是没摸准这些方法的触发场景,咱们一步步来:
iOS系统并没有提供直接的API来监听本地通知是否已经在屏幕上显示,但我们可以通过两个间接手段来实现类似效果:
结合系统已送达通知列表+本地持久化记录
首先,给每个本地通知分配唯一的identifier,调度通知时把这个ID和预期触发时间存在UserDefaults或本地数据库里。之后可以调用getDeliveredNotifications获取当前还留在通知中心的通知,对比ID就能判断是否已显示;如果需要记录用户是否看过(哪怕通知被清除),还要在用户点击通知或APP启动时更新本地记录的状态。示例代码:
// 1. 调度通知时记录基础信息 let notificationID = "reminder_20240520_001" let content = UNMutableNotificationContent() content.title = "待办提醒" content.userInfo = ["taskID": "123"] // 自定义数据 let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false) let request = UNNotificationRequest(identifier: notificationID, content: content, trigger: trigger) UNUserNotificationCenter.current().add(request) { error in guard error == nil else { print("添加通知失败:\(error!.localizedDescription)") return } // 记录通知ID和调度时间 UserDefaults.standard.set(Date(), forKey: "scheduled_\(notificationID)") } // 2. 检查通知是否已显示(存在于通知中心) UNUserNotificationCenter.current().getDeliveredNotifications { deliveredNotifications in let isDisplayed = deliveredNotifications.contains { $0.request.identifier == notificationID } print("该通知是否已显示:\(isDisplayed)") }利用通知点击回调更新状态
如果用户点击了通知,在UNUserNotificationCenterDelegate的didReceive response方法里,我们可以标记该通知为“已查看”,这样后续就能通过本地记录判断用户是否看到过这条通知。
这个需求分两种场景处理:
场景1:用户点击通知唤醒APP(包括终止状态启动)
这种情况是可以直接拿到通知数据的,关键是正确设置代理并实现对应方法:
首先在
AppDelegate或SceneDelegate的启动方法里设置通知中心代理:func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // 请求通知权限 UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in // 处理权限结果 } // 设置代理 UNUserNotificationCenter.current().delegate = self return true }实现
UNUserNotificationCenterDelegate的回调方法:extension AppDelegate: UNUserNotificationCenterDelegate { // 当用户点击通知(无论APP是后台/终止状态),都会触发这个方法 func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { // 获取通知的自定义数据 let userInfo = response.notification.request.content.userInfo print("收到通知数据:\(userInfo)") // 这里可以处理业务逻辑,比如跳转对应页面 completionHandler() } // 如果需要APP在前台时也显示通知,实现这个方法 func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { completionHandler([.banner, .sound]) } }另外,如果APP是从终止状态被通知唤醒,也可以在
didFinishLaunchingWithOptions里通过launchOptions[.localNotification]获取旧版通知数据,但iOS 10+更推荐用上面的代理方法。
场景2:用户没点击通知,仅锁屏显示通知
这种情况系统不会主动唤醒APP,所以无法实时获取通知数据。我们只能在下次APP启动时,通过之前记录的通知触发时间,结合getDeliveredNotifications来判断哪些通知已经触发并显示过,再提取对应数据。
你提到的present应该是指willPresent方法,它只有APP在前台运行时才会触发;而didReceive如果是指didReceive response,则只有用户点击通知时才会触发——如果APP在后台/终止且用户没点击通知,这两个方法都不会被调用,所以你才会觉得没生效。
内容的提问来源于stack exchange,提问作者Irwan Madness

