我刚刚设法解决了同样的问题,并且非常乐意分享我是如何做到的(从 iOS 9.3 开始)。
就我而言,我使用单个自定义按钮来启用具有三种可能状态的通知:默认(意味着尚未提示用户启用通知)、已完成(已提示用户并同意获取通知)和失败(用户拒绝通知提示)。该按钮仅在默认状态下启用。
现在,我在这里使用的不是单一技术,而是几个(尽管相关的)调用的组合。
逻辑如下:即使用户拒绝了通知提示(直到用户删除并重新安装应用程序才出现一次),我们仍然注册远程通知。该过程将照常进行,设备将被注册,但在发布新通知时用户不会收到任何通知。然后,我们可以利用了解当前通知设置以及用户是否已注册远程通知来了解他们是否曾经收到提示(因此按钮获得默认状态)。
这种方法并非完美无缺。如果用户最初同意接收通知,但后来决定从“设置”手动关闭它们,则该按钮将设置为默认状态,但在激活后不会提示用户再次启用通知。但在大多数情况下,这无关紧要,因为这种 UI 通常只在入职/注册过程中显示一次。
至于代码本身(Swift 2.2):
func updateButtonStatus() {
// as currentNotificationSettings() is set to return an optional, even though it always returns a valid value, we use a sane default (.None) as a fallback
let notificationSettings: UIUserNotificationSettings = UIApplication.sharedApplication().currentUserNotificationSettings() ?? UIUserNotificationSettings(forTypes: [.None], categories: nil)
if notificationSettings.types == .None {
if UIApplication.sharedApplication().isRegisteredForRemoteNotifications() {
// set button status to 'failed'
} else {
// set button status to 'default'
}
} else {
// set button status to 'completed'
}
}
我们从视图控制器的viewWillAppear(animated) 实现中调用此方法。
此时还需要做一些其他事情:首先,每当按钮被触摸时(仅在其默认状态下才会发生),我们必须提示用户接受或拒绝通知,我们还希望我们的无论用户选择什么,UI 都能做出正确反应:
@IBAction func notificationsPermissionsButtonTouched(sender: AnyObject) {
let settings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
}
然后,我们需要实现正确的UIApplicationDelegate 方法来处理事件。由于没有针对这些的全局UIApplication 通知,我们发送自己的通知:
// AppDelegate.swift
func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings) {
application.registerForRemoteNotifications()
if notificationSettings.types == .None {
NSNotificationCenter.defaultCenter().postNotificationName("ApplicationDidFailToRegisterUserNotificationSettingsNotification", object: self)
}
}
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
NSNotificationCenter.defaultCenter().postNotificationName("ApplicationDidRegisterForRemoteNotificationsNotification", object: self)
}
现在回到我们的视图控制器,我们需要处理这些通知。所以,在我们的viewWillAppear(animated) 和viewWillDisappear(animated) 实现中,我们这样做:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(PermissionsViewController.applicationDidRegisterForRemoteNotificationsNotification(_:)), name: "ApplicationDidRegisterForRemoteNotificationsNotification", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(PermissionsViewController.applicationDidFailToRegisterUserNotificationSettingsNotification(_:)), name: "ApplicationDidFailToRegisterUserNotificationSettingsNotification", object: nil)
updateButtonStatus()
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self, name: "ApplicationDidRegisterForRemoteNotificationsNotification", object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self, name: "ApplicationDidFailToRegisterUserNotificationSettingsNotification", object: nil)
}
还有通知处理程序本身:
func applicationDidRegisterForRemoteNotificationsNotification(notification: NSNotification) {
let notificationSettings: UIUserNotificationSettings = UIApplication.sharedApplication().currentUserNotificationSettings() ?? UIUserNotificationSettings(forTypes: [.None], categories: nil)
if notificationSettings.types != .None {
// set button status to 'completed'
}
}
func applicationDidFailToRegisterUserNotificationSettingsNotification(notification: NSNotification) {
// set button status to 'failed'
}
奖金
如果用户拒绝了通知提示,并且我们希望有一个按钮将他们引导到设置面板,他们可以重新启用它,并让我们的 UI 做出相应的反应,该怎么办?嗯,很高兴你问。
在“设置”中,有一个鲜为人知的机制可以深度链接到您的“应用”部分(它从 iOS 8 开始就存在,但直到几个小时前我才有机会了解它)。在我们的 settings 按钮触摸处理程序中,我们这样做:
@IBAction func settingsButtonTouched(sender: AnyObject) {
if let settingsURL = NSURL(string: UIApplicationOpenSettingsURLString) {
UIApplication.sharedApplication().openURL(settingsURL)
}
}
由于我们想要更新我们的 UI 以反映用户可能做出的任何更改,因此我们在 viewWillAppear(animated) 实现中为 UIApplicationDidBecomeActiveNotification 添加了一个通知侦听器(不要忘记从 viewWillDisapper(animated) 中删除侦听器。并且最后,在相应的通知处理程序方法中,我们只需调用现有的updateButtonStatus()。