iOS 9:如何检测用户何时对推送通知请求说“不允许”?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/35126361/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 08:23:47  来源:igfitidea点击:

iOS 9: How to detect when user said 'Don't Allow' to the push notification request?

iosobjective-capple-push-notifications

提问by goelv

In iOS 9, is there a system level callback I can read which tells me whether the user has tapped on 'Don't allow' on the push notification request?

在 iOS 9 中,是否有我可以阅读的系统级回调,它告诉我用户是否在推送通知请求上点击了“不允许”?

I prompt the user with a custom screen informing them about push notifications and the value it has in my app.

我用自定义屏幕提示用户,通知他们推送通知及其在我的应用程序中的价值。

Custom Tripnary Prompt

Custom Tripnary Prompt

They have two choices, yes or no. If they select Yes, I request the operating system for push notification and they see a pop up like the image below.

他们有两个选择,是或否。如果他们选择是,我请求操作系统提供推送通知,他们会看到如下图所示的弹出窗口。

iOS Level system prompt for push notifications

iOS Level system prompt for push notifications

Now, if the user taps on YES, then there is a function called didRegisterForRemoteNotificationsWithDeviceTokenwhich tells me the this device has been registered for push notifications. I can use this to move ahead to the next screen (and take them into the first screen after signing up)

现在,如果用户点击“是”,则会调用一个函数didRegisterForRemoteNotificationsWithDeviceToken,告诉我该设备已注册推送通知。我可以用它来前进到下一个屏幕(并在注册后将它们带入第一个屏幕)

However, how do I detect if the user taps on DON'T allow?I need to know that so I can accordingly move the user to the next screen (and take them into the first screen after signing up). The function didFailToRegisterForRemoteNotificationsWithErroris not called if the user taps on 'Don't Allow'.

但是,如何检测用户是否点击了不允许?我需要知道这一点,以便我可以相应地将用户移动到下一个屏幕(并在注册后将他们带到第一个屏幕)。didFailToRegisterForRemoteNotificationsWithError如果用户点击“不允许”,则不会调用该函数。

This question is not a duplicate because the answer accepted for that question is specific to iOS 7, where as my question is specific is iOS 9.

这个问题不是重复的,因为该问题接受的答案特定于 iOS 7,而我的问题是特定于 iOS 9。

回答by liamnichols

As of iOS 8, the notification registration process changed and moved away from the user having to grant permission to just remotenotifications.

从 iOS 8 开始,通知注册过程发生了变化,不再需要用户授予远程通知权限。

You can now technically register for remote notifications without having to get permission from the user. What you do need permission for is the user notification settings (alerts, sounds and badges). These are now generic to both local and remote notifications making the other answers technically incorrect.

您现在可以在技术上注册远程通知,而无需获得用户的许可。您需要许可的是用户通知设置(警报、声音和徽章)。这些现在对于本地和远程通知都是通用的,使其他答案在技术上不正确。

You request permission via the -[UIApplication registerUserNotificationSettings:]method on UIApplicationand as per the documentation, you get a callback to the -[UIApplicationDelegate application: didRegisterUserNotificationSettings:]delegate method.

您通过-[UIApplication registerUserNotificationSettings:]方法请求许可,UIApplication并根据文档,您将获得对-[UIApplicationDelegate application: didRegisterUserNotificationSettings:]委托方法的回调。

In the header, there is a comment saying the following:

在标题中,有一条评论如下:

// This callback will be made upon calling -[UIApplication registerUserNotificationSettings:]. The settings the user has granted to the application will be passed in as the second argument.

This means that if the user did not grant permissions for notifications (both local and remote) then the second parameter won't contain any values.

这意味着如果用户没有授予通知权限(本地和远程),那么第二个参数将不包含任何值。



-[UIApplication isRegisteredForRemoteNotifications]will just tell you if the applicaiton has actually registered with Apple's push servers and has received a device token:

-[UIApplication isRegisteredForRemoteNotifications]只会告诉您应用程序是否已实际注册到 Apple 的推送服务器并已收到设备令牌:

Return Value
YES if the app is registered for remote notifications and received its device token or NO if registration has not occurred, has failed, or has been denied by the user.

返回值
YES 如果应用程序注册了远程通知并收到了它的设备令牌,或者 NO 如果注册没有发生、失败或被用户拒绝。

It's worth reading the UIApplicationdocumentation as it has all the info you need.

值得阅读UIApplication文档,因为它包含您需要的所有信息。

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/#//apple_ref/occ/instm/UIApplication

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/#//apple_ref/occ/instm/UIApplication

回答by boliva

I just managed to solve this very same issue and would be more than happy to share how I did it (as of iOS 9.3).

我只是设法解决了这个非常相同的问题,并且很乐意分享我是如何做到的(从 iOS 9.3 开始)。

In my case, I am using a single custom button to enable notifications with three possible states: default (meaning that the user hasn't been prompted yet to enable notifications), completed (the user has been prompted and agreed to get notifications) and failed (the user rejected the notifications prompt). The button is only enabled while in the default state.

就我而言,我使用单个自定义按钮启用具有三种可能状态的通知:默认(意味着尚未提示用户启用通知)、已完成(已提示用户并同意接收通知)和失败(用户拒绝通知提示)。该按钮仅在处于默认状态时启用。

Now, I am not using a single technique here but a combination of a few (albeit related) calls.

现在,我在这里不使用单一技术,而是使用一些(尽管相关)调用的组合。

The logic is as follows: even if the user rejects the notifications prompt (which only appears once until the user deletes and reinstalls the App), we still register for remote notifications. The process will continue as usual, the device will get registered but the user won't get any notice when a new notification is posted. We can then take advantage of knowing both the current notification settings and whether the user is already registered for remote notifications to know if they have ever been prompted (so the button gets the default status).

逻辑是这样的:即使用户拒绝通知提示(在用户删除并重新安装App之前只出现一次),我们仍然注册远程通知。该过程将照常继续,设备将被注册,但在发布新通知时用户不会收到任何通知。然后,我们可以利用了解当前通知设置以及用户是否已经注册远程通知来了解他们是否曾经收到过提示(因此按钮获得默认状态)。

This method isn't flawless. If the user initially agrees to get notifications, but later on decides to manually turn them off from Settings, then the button will be set to the default state but, upon activation, won't prompt the user for notifications to be enabled again. But in most cases this shouldn't matter, as this kind of UI is usually shown once during the onboarding/sign up process only.

这种方法并非完美无缺。如果用户最初同意接收通知,但后来决定从“设置”手动关闭它们,则该按钮将设置为默认状态,但在激活时不会提示用户再次启用通知。但在大多数情况下,这无关紧要,因为这种 UI 通常只在入职/注册过程中显示一次。

As for the code itself (Swift 2.2):

至于代码本身(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'
    }
}

We call this method from our view controller's viewWillAppear(animated)implementation.

我们从视图控制器的viewWillAppear(animated)实现中调用此方法。

At this point a few more other things need to happen: first, whenever the button is touched (which will only occur while in its default state) we must prompt the user to either accept or reject notifications, and we also want our UI to react properly, whatever the user chooses:

在这一点上还有一些其他事情需要发生:首先,每当按钮被触摸时(只会在其默认状态下发生)我们必须提示用户接受或拒绝通知,我们还希望我们的 UI 做出反应正确地,无论用户选择什么:

@IBAction func notificationsPermissionsButtonTouched(sender: AnyObject) {
    let settings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
    UIApplication.sharedApplication().registerUserNotificationSettings(settings)
}

And then, we need to implement the proper UIApplicationDelegatemethods to handle the event. Since there are no global UIApplicationnotifications for these, we send our own ones:

然后,我们需要实现正确的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)
}

Now back to our view controller, we need to handle those notifications. So, in our viewWillAppear(animated)and viewWillDisappear(animated)implementations, we do:

现在回到我们的视图控制器,我们需要处理这些通知。因此,在我们的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)
}

And the notification handlers themselves:

和通知处理程序本身:

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'
}


Bonus

奖金

What if the user rejected the notifications prompt and we want to have a button to guide them to the Settings panel where they can re-enable it, and make our UI react accordingly? Well, I'm glad you asked.

如果用户拒绝了通知提示,而我们想要一个按钮来引导他们进入设置面板,他们可以在那里重新启用它,并使我们的 UI 做出相应的反应,该怎么办?嗯,我很高兴你问。

There is a very little known mechanism to deep-link to your App section inside Settings (it's been there since iOS 8, but I hadn't had the chance to learn about it until a few hours ago). In our settingsbutton touch handler we do this:

有一个鲜为人知的机制可以在“设置”中深度链接到您的“应用程序”部分(自 iOS 8 以来就存在,但我直到几个小时前才有机会了解它)。在我们的设置按钮触摸处理程序中,我们这样做:

@IBAction func settingsButtonTouched(sender: AnyObject) {
    if let settingsURL = NSURL(string: UIApplicationOpenSettingsURLString) {
        UIApplication.sharedApplication().openURL(settingsURL)
    }
}

Since we want to update our UI to reflect whatever changes the user might have made, we add a notification listener for UIApplicationDidBecomeActiveNotificationin our viewWillAppear(animated)implementation (don't forget to remove the listener from viewWillDisapper(animated). And finally, from inside the corresponding notification handler method we just call our existing updateButtonStatus().

由于我们想要更新我们的 UI 以反映用户可能做出的任何更改,因此我们UIApplicationDidBecomeActiveNotification在我们的viewWillAppear(animated)实现中添加了一个通知侦听器(不要忘记从 中删除侦听器viewWillDisapper(animated)。最后,从我们刚刚调用的相应通知处理程序方法内部我们现有的updateButtonStatus().

回答by Eli Braginskiy

In your app delegate use this method

在您的应用程序委托中使用此方法

- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings

- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings

then you can know if the user gave notification permissions by using

然后您可以知道用户是否通过使用授予通知权限

[[UIApplication sharedApplication] isRegisteredForRemoteNotifications]

[[UIApplication sharedApplication] isRegisteredForRemoteNotifications]

or use the notificationSettingsyou receive.

或使用notificationSettings您收到的。

回答by Dharmesh Siddhpura

No there is no way to detect push notification from APNS in the application if it is disallowed.

不,如果不允许,则无法在应用程序中检测来自 APNS 的推送通知。

Use this code to check if it is allowed and navigate the app to enable it:

使用此代码检查是否允许并导航应用程序以启用它:

UIRemoteNotificationType types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
if (types == UIRemoteNotificationTypeNone)
{
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"app-settings:"]];
}

Hope this helps!

希望这可以帮助!

回答by ajmccall

There is a quick and cheap way to do this. iOS9 this delegate method

有一种快速且廉价的方法可以做到这一点。iOS9 这个委托方法

- (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken

is called oncewhen the dialogue is shown, and then a secondtime when the user taps on "Ok". Simply add a flag here.

在显示对话时调用一次,然后在用户点击"Ok"时调用第二次。只需在此处添加一个标志。

Then, whenever you wish to display the custom "remind user how to enable push" message, simply check the flag and your current notification settings (as detailed by many of the answers above).

然后,每当您希望显示自定义“提醒用户如何启用推送”消息时,只需检查标志和您当前的通知设置(如上面的许多答案所详述)。

- (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {

    self.pushDialogShown = YES;
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 

    // not a great place to put this logic. For demonstration purposes ONLY
    if (self.pushDialogueShown && ![self pushMessageEnabled]) {
        [self showPushReminderMessage];
    }
}

回答by Josh O'Connor

Use:

用:

[[UIApplication sharedApplication] isRegisteredForRemoteNotifications];

[[UIApplication sharedApplication] isRegisteredForRemoteNotifications];

and NSUserDefaults. Store a key (e.g HasSeenSystemPushNotification) to truewhen the system dialog for push notifications is presented.

NSUserDefaults。当推送通知的系统对话框出现时,存储一个密钥(例如HasSeenSystemPushNotificationtrue

Then you can check the NSUD key and the isRegisteredForRemoteNotifications bool to see whether it has been presented/accepted and do your work accordingly.

然后,您可以检查 NSUD 密钥和 isRegisteredForRemoteNotifications bool 以查看它是否已呈现/接受并相应地执行您的工作。

回答by Vizllx

Check by this method:-

通过这种方法检查:-

[[UIApplication sharedApplication] isRegisteredForRemoteNotifications];

But if your app supports less than iOS 8 version then you have to do the checking like this:-

但是,如果您的应用支持低于 iOS 8 的版本,那么您必须像这样进行检查:-

UIRemoteNotificationType types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
if (types == UIRemoteNotificationTypeNone) 
{ 
  //notification is not enabled by user

}