Mac OS X NSUserNotificationCenter通知获取关闭事件/回调

14

在我们的应用中,我们以提示框的形式显示通知中心通知。

显示通知正常工作,当用户通过点击通知或点击操作按钮与通知交互时,我们会获得回调。

然而,我们希望在用户单击通知中的其他按钮时获得回调或事件。我看到MAC OS在显示其可用更新对话框时就这样做了。

有关OS X可用更新警告的澄清,请参阅此图像:

enter image description here

我已在互联网上搜索过这个问题,并查看了 Notification Center 的文档(链接1)(链接2)

是否有未记录的 API?或者一些自定义机制来检测单击其他(关闭)按钮?

4个回答

9
虽然另一个(关闭)按钮明显是用来关闭通知的,无论其自定义标题如何,但用户通过单击关闭按钮关闭通知时,没有一种优雅的方法来通知此操作。但您可以监视默认用户通知中心的 deliveredNotifications 属性:只要通知尚未被解除,数组就会包含该通知。一旦通知被解除,该数组就不再包含它了。这可以在 NSUserNotificationCenter 委托方法中实现,例如:
- (void)userNotificationCenter:(NSUserNotificationCenter *)center didDeliverNotification:(NSUserNotification *)notification
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
                   ^{
                       BOOL notificationStillPresent;
                       do {
                           notificationStillPresent = NO;
                           for (NSUserNotification *nox in [[NSUserNotificationCenter defaultUserNotificationCenter] deliveredNotifications]) {
                               if ([nox.identifier isEqualToString:notification.identifier]) notificationStillPresent = YES;
                           }
                           if (notificationStillPresent) [NSThread sleepForTimeInterval:0.20f];
                       } while (notificationStillPresent);
                       dispatch_async(dispatch_get_main_queue(), ^{
                           [self notificationHandlerForNotification:notification];
                       });
                   });
}

这段代码每隔200毫秒检查一次通知是否存在。一旦通知消失,主线程将调用-notificationHandler:方法,这只是一个任意的回调方法。
在自定义的-notificationHandler:方法中,您可以检查NSUserNotificationCenter的didActivateNotification:代理方法是否已为该通知调用。如果没有,则用户很可能点击了通知关闭按钮。
但是,这并不是完全可靠的,因为用户也可能以其他方式解除通知,即没有点击关闭按钮。

谢谢你的帮助。现在我已经有了想法并尝试了你的解决方案。我现在也理解了 deliveredNotifications 属性。那么 "identifier" 是什么?你是指一些自定义的属性吗?我需要在这个对象上定义吗?XCode 说对象 NSUserNotification 上找不到属性。抱歉,但我对这个主题还有点新。 - AUR
当运行OS X 10.8时,您将不得不使用另一种方法来比较通知,例如通过它们的userInfo属性,您还可以存储一些唯一标识符,但一般思路保持不变。 - Tim
使用isEqual:比较通知似乎是一个更好的想法: 如果([nox.identifier isEqual:notification]),则notificationStillPresent = YES; - JasonZ
1
我无法让它工作。我更改了if语句,以检查当前的[[NSUserNotificationCenter defaultUserNotificationCenter] deliveredNotifications].count是否大于显示通知之前的[[NSUserNotificationCenter defaultUserNotificationCenter] deliveredNotifications].count。这样做效果很好。谢谢! - Daniel Storm
1
@DanielStorm 但这并没有告诉你是哪个通知被移除了。 - maxisme
显示剩余3条评论

2

在Swift 3中

func userNotificationCenter(_ center: NSUserNotificationCenter, didDismissAlert notification: NSUserNotification) {
        print("dismissed")
    }

这不是 NSUserNotificationDelegate 的一部分,但可以完美地工作。


1
@CristiBăluță 如果你正在使用Swift >=4,请确保你使用了@objc注释;否则Objective-C运行时将无法看到你的委托方法。 - Felix J. Acero

1
这帮助了我。
func userNotificationCenter(_ center: NSUserNotificationCenter, didActivate notification: NSUserNotification) {
    switch (notification.activationType) {
    case .none:
        print("none CLicked")
        break
    case .actionButtonClicked:
        print("Additional Action Clicked")
        break
    case .contentsClicked:
        print("contents CLicked")
        break
    case .replied:
        print("replied Action Clicked")
        break
    case .additionalActionClicked:
        print("Additional  MENU  Action Clicked")
        break
    }

1
在Swift 2.3中:
func userNotificationCenter(center: NSUserNotificationCenter, didDeliverNotification notification: NSUserNotification) {

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { 
        var notificationStillPresent: Bool
        repeat {
            notificationStillPresent = false
            for nox in NSUserNotificationCenter.defaultUserNotificationCenter().deliveredNotifications {
                if nox.identifier == notification.identifier {
                    notificationStillPresent = true
                    break
                }
            }

            if notificationStillPresent {
                let _ = NSThread.sleepForTimeInterval(0.20)
            }
        } while notificationStillPresent

        dispatch_async(dispatch_get_main_queue()) {
            self.notificationHandlerFor(notification)
        }
    }
}

PS:请注意,这是检测解除事件的方法,可能会在多种情况下触发。

  1. 点击 otherButton 解除
  2. 在通知中心中点击 Clear All 按钮

PS 2:如果您正在使用 deliveryRepeatInterval,比如 1 分钟,则在 deliveredNotifications 数组中有多个通知,但只显示一个。解除应触发多个回调。

PS 3:点击 actionButton 也会触发解除回调。


目前我正在尝试区分单个通知被解除(用户按下通知左上角的小X)和“全部清除”操作(用户点击堆叠线程上的X,然后点击出现的“全部清除”)。但是一直没有成功。我得到了相同的actionID - 以及线程中某个通知的任意响应+请求。这真让我困惑。 - Motti Shneor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接