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

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方法里,我们可以标记该通知为“已查看”,这样后续就能通过本地记录判断用户是否看到过这条通知。

二、锁屏/APP终止状态下获取本地通知数据

这个需求分两种场景处理:

场景1:用户点击通知唤醒APP(包括终止状态启动)

这种情况是可以直接拿到通知数据的,关键是正确设置代理并实现对应方法:

  1. 首先在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
    }
    
  2. 实现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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:54:28