ios 用户拒绝推送通知提示时的回调方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19053092/
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 01:46:50  来源:igfitidea点击:

Callback Method if user declines Push Notification Prompt?

iosobjective-cpush-notificationapple-push-notifications

提问by u635504

My problem is I want to show a loading screen for the initial Push Notification Prompt "The app wants to send you push notifications."

我的问题是我想为初始推送通知提示“应用程序想要向您发送推送通知”显示加载屏幕。

So if the user hits yesI can proceed and start the app in the then invoked delegate methods:

因此,如果用户点击,yes我可以继续并在随后调用的委托方法中启动应用程序:

- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
  [self hideLoadingScreen];
}

- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error
{
  [self hideLoadingScreen];
}

However if the user hits no, none of these methods get called, which makes sense. My question is, is there a different delegate method that gets fired if he declines?

但是,如果用户点击no,这些方法都不会被调用,这是有道理的。我的问题是,如果他拒绝,是否有不同的委托方法会被解雇?

My problem is if nois selected, the loading screens never disappear.So I somehow need to know when the user is done with the selection.

我的问题是如果no被选中,加载屏幕永远不会消失。所以我需要知道用户何时完成了选择。

采纳答案by Jeff Mascia

In iOS 7, when the system's push notification prompt appears, the app becomes inactive and UIApplicationWillResignActiveNotification fires. Similarly when the user responds to the prompt (pressing either Yes or No), the app becomes active again and UIApplicationDidBecomeActiveNotification fires.

在 iOS 7 中,当系统的推送通知提示出现时,应用程序将变为非活动状态并触发 UIApplicationWillResignActiveNotification。类似地,当用户响应提示(按 Yes 或 No)时,应用程序将再次激活并触发 UIApplicationDidBecomeActiveNotification。

So you can listen for this notification, and then hide your loading screen.

所以你可以收听这个通知,然后隐藏你的加载屏幕。

Note: While the prompt is displayed, the Home button, Notification Center, and Control Center are disabled so they cannot trigger a false-positive UIApplicationDidBecomeActiveNotification. However if the user presses Lock button it will trigger UIApplicationDidBecomeActiveNotification.

注意:当显示提示时,主页按钮、通知中心和控制中心被禁用,因此它们不会触发误报 UIApplicationDidBecomeActiveNotification。但是,如果用户按下锁定按钮,它将触发 UIApplicationDidBecomeActiveNotification。

回答by Grzegorz Krukowski

You can always get current allowed notification types from:

您始终可以从以下位置获取当前允许的通知类型:

UIRemoteNotificationType notificationTypes = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];

Keep in mind user can also disable notification in phone settings.

请记住,用户还可以在手机设置中禁用通知。

If you check that on didRegisterForRemoteNotificationsWithDeviceToken you should see if types you asked for are enabled.

如果您在 didRegisterForRemoteNotificationsWithDeviceToken 上检查,您应该查看您要求的类型是否已启用。

回答by JAL

Here's how I did it in Swift 3. They key here is to keep track of the application's lifecycle state internally. When the push prompt is presented, the application resigns active, but does not enter the background. This is all in my AppDelegate.swift.

下面是我在 Swift 3 中的做法。这里的关键是在内部跟踪应用程序的生命周期状态。当出现推送提示时,应用程序退出活动,但不进入后台。这一切都在我的 AppDelegate.swift 中。

This is a really big hack and is not recommended in production. Apple could change the way these alerts are presented and this could break at any time. This was tested using various iPhones and iPads running iOS 9 and 10.

这是一个非常大的 hack,不建议在生产中使用。Apple 可以改变这些警报的呈现方式,而且这种方式可能随时中断。这是使用运行 iOS 9 和 10 的各种 iPhone 和 iPad 进行的测试。

/// An internal value used to track application lifecycle state
enum ApplicationLifecycleState {
    case willResignActive
    case didEnterBackground
    case willEnterForeground
    case didBecomeActive
    case unknown
}

/// This is used purely for tracking the application lifecycle for handling the system push notification alert
var internalLifecycleState: ApplicationLifecycleState = .unknown {
    didSet {
        // If we're not in the middle of asking for push permissions, none of the below applies, just bail out here
        if !isAskingForPushPermissions { return }

        // WARNING: Application lifecycle trickery ahead
        // The normal application lifecycle calls for backgrounding are as follows:
        // applicationWillResignActive -> applicationDidEnterBackground -> applicationWillEnterForeground -> applicationDidBecomeActive
        // However, when the system push notification alert is presented, the application resigns active, but does not enter the background:
        // applicationWillResignActive -> [user taps on alert] -> applicationDidBecomeActive
        // We can use this discrepancy to our advantage to detect if the user did not allow push permissions

        // If applicationDidBecomeActive
        // AND the previous state was applicationWillResignActive
        // AND the notification types bitmask is 0, we know that the user did not allow push permissions
        // User denied permissions
        if internalLifecycleState == .didBecomeActive
            && oldValue == .willResignActive
            && UIApplication.shared.currentUserNotificationSettings?.types.rawValue == 0 {
            // We're done
            firePushCompletionBlockAndCleanup(registered: false)
        } else {
            // The state below can only be entered on iOS 10 devices.
            // If the user backgrounds the app while the system alert is being shown,
            // when the app is foregrounded the alert will dismiss itself without user interaction.
            // This is the equivalent of the user denying push permissions.
            // On iOS versions below 10, the user cannot background the app while a system alert is being shown.

            if #available(iOS 10, *), internalLifecycleState == .didBecomeActive {
                firePushCompletionBlockAndCleanup(registered: false)
            }
        }
    }
}

/// Used internally to track if the system push notification alert is currently being presented
var isAskingForPushPermissions = false

typealias PushNotificationRegistrationCompletionBlock = ((_ registered: Bool) -> Void)

// ...

func applicationWillResignActive(_ application: UIApplication) {    
    internalLifecycleState = .willResignActive
}

func applicationDidEnterBackground(_ application: UIApplication) {
    internalLifecycleState = .didEnterBackground
}

func applicationWillEnterForeground(_ application: UIApplication) {
    internalLifecycleState = .willEnterForeground
}

func applicationDidBecomeActive(_ application: UIApplication) {
    internalLifecycleState = .didBecomeActive
}

// ...

func setupPushNotifications(_ application: UIApplication = UIApplication.shared, completion: @escaping PushNotificationRegistrationCompletionBlock) {
    isAskingForPushPermissions = true
    pushCompletionBlock = completion
    let settings = UIUserNotificationSettings(types: [.alert, .sound, .badge], categories: nil)
    application.registerUserNotificationSettings(settings)
    application.registerForRemoteNotifications()
}

fileprivate func firePushCompletionBlockAndCleanup(registered: Bool) {
    pushCompletionBlock?(registered)
    pushCompletionBlock = nil
    isAskingForPushPermissions = false
}

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {

    // application:didRegisterForRemoteNotificationsWithDeviceToken may be called more than once (once for each notification type)
    // By checking that the notification types bitmask is greater than 0, we can find the final time this is called (after the user actually tapped "allow")
    // If the user denied push permissions, this function is never called with a positive notification type bitmask value
    if UIApplication.shared.currentUserNotificationSettings?.types.rawValue ?? 0 > 0 {
        firePushCompletionBlockAndCleanup(registered: true)
    }
}

func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
    print("Failed to register for notifications with error: " + error.localizedDescription)
    firePushCompletionBlockAndCleanup(registered: false)
}

Usage:

用法:

appDelegate.setupPushNotifications(completion: { [weak self] (registered) in
    // If registered is false, the user denied permissions
})

回答by NYC Tech Engineer

Couldn't you just do the following:

你不能只做以下事情:

- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
    BOOL pushEnabled = notificationSettings.types & UIUserNotificationTypeAlert;
}

This method should be the callback to that push notifications prompt, and from there, you can check the bitmask to see if push notifications were enabled or not.

这个方法应该是那个推送通知提示的回调,从那里,你可以检查位掩码,看看是否启用了推送通知。

回答by Nermin Sehic

Some of the answers here are not relevant anymore, or are more complicated than it should be, since UserNotifications framework and iOS 10 you can easily get this data like so:

这里的一些答案不再相关,或者比应该的更复杂,因为 UserNotifications 框架和 iOS 10 你可以很容易地获得这些数据,如下所示:

let center = UNUserNotificationCenter.current()

// Request permission to display alerts and play sounds.
center.requestAuthorization(options: [.alert, .sound]) 
{ (granted, error) in
  // Enable or disable features based on authorization.
}

回答by Mussa Charles

2nd May 2019

2019 年 5 月 2 日

This is the implementation to check if notifications are authorized any time in your app, Simple call this function.

这是在您的应用程序中随时检查通知是否获得授权的实现,简单调用此函数。

    private func checkNotificationsAuthorizationStatus() {
    let userNotificationCenter = UNUserNotificationCenter.current()
    userNotificationCenter.getNotificationSettings { (notificationSettings) in
        switch notificationSettings.authorizationStatus {
        case .authorized:
            print("The app is authorized to schedule or receive notifications.")
        case .denied:
            print("The app isn't authorized to schedule or receive notifications.")

        case .notDetermined:
            print("The user hasn't yet made a choice about whether the app is allowed to schedule notifications.")
        case .provisional:
            print("The application is provisionally authorized to post noninterruptive user notifications.")
        }
    }

}

回答by davidrynn

For Swift 3and Swift 4.0Using NotificationCenter and the AppDelegate method didRegister notificationSettings. NotificationSettings show whether the users opted for badges, sounds, etc. and will be an empty array if they declined push notifications. It is fired specifically when users respond to the push notifications prompt and seems to be what most devs use, since it's more specific than checking didBecomeActive. But Apple might change this. Who knows?

对于Swift 3Swift 4.0使用 NotificationCenter 和 AppDelegate 方法didRegister notificationSettings。NotificationSettings 显示用户是否选择了徽章、声音等,如果他们拒绝推送通知,它将是一个空数组。它会在用户响应推送通知提示时专门触发,并且似乎是大多数开发人员使用的,因为它比检查 didBecomeActive 更具体。但苹果可能会改变这一点。谁知道?

Unfortunately, NotificationCenter does not have a preset notification name so you either have to setup and extension (see end) or use the raw value in (SO has more on this).

不幸的是,NotificationCenter 没有预设的通知名称,因此您要么必须设置和扩展(见结尾),要么使用中的原始值(SO 对此有更多说明)。

In AppDelegate:

在 AppDelegate 中:

    func application(_ application: UIApplication, didRegister notificationSettings: UIUserNotificationSettings) {
      // if not registered users will have an empty set of settings
      let accepted: Bool = !notificationSettings.types.isEmpty
      NotificationCenter.default.post(name: Notification.Name(rawValue: "didRespondToPrompt"), object: self, userInfo: ["didAccept" : accepted])
}

Then observe wherever you need to, for example in a view controller:

然后在需要的地方观察,例如在视图控制器中:

class MyViewController: UIViewController {

//MARK: - Lifecycle
   override func viewDidLoad() {
      super.viewDidLoad()
      NotificationCenter.default.addObserver(self, selector: #selector(MyViewController.didRespondToPushPrompt(_:)), name: NSNotification.Name(rawValue: "didRespondToPrompt"), object: nil)

   }
    @objc func didRespondToPushPrompt(_ notification: Notification) {

       if let userInfo: [AnyHashable : Any] = notification.userInfo, let didAccept: Bool = userInfo[NSNotificationKeyNames.didAccept] as? Bool, !didAccept {
        //if user doesn't accept, do this...

       } else  {
       //all other situations code goes here
      }

   }
}

Couple of things: First, for Swift 4.0, I'm using "@objc" in front of one method, but it's not necessary for Swift 3.
Also, for using NotificationCenter, in practice I did not use "rawValue". Instead I made an extension like so:

有几件事:首先,对于 Swift 4.0,我在一个方法前面使用了“@objc”,但对于 Swift 3 来说不是必需的。
此外,对于使用 NotificationCenter,实际上我没有使用“rawValue”。相反,我做了一个这样的扩展:

import Foundation

extension NSNotification.Name {
   static let DidRegisterForPushNotifications = NSNotification.Name("DidRegisterForPushNotifications")
}

Which I could then use like so:

然后我可以像这样使用:

NotificationCenter.default.post(name: Notification.Name.DidRegisterForPushNotifications, object: self, userInfo: ["didAccept" : myBool])etc., etc.

NotificationCenter.default.post(name: Notification.Name.DidRegisterForPushNotifications, object: self, userInfo: ["didAccept" : myBool])等等等等。

回答by Finskii

You can detect if user has cancelled the notification prompt in didRegisterUserNotificationSettingsmethod that fires after calling registerForRemoteNotificationTypesby checking the notificationSettings.types.

您可以检测,如果用户已经取消了通知,提示didRegisterUserNotificationSettings方法调用后,大火registerForRemoteNotificationTypes被检查notificationSettings.types

If you have requested a number of settings but notificationSettings.types == UIUserNotificationTypeNonemeans, that user has cancelled the prompt.

如果您请求了许多设置但notificationSettings.types == UIUserNotificationTypeNone意味着该用户已取消提示。

But don't forget that registerForRemoteNotificationTypesmethod is now deprecated!

但是不要忘记该registerForRemoteNotificationTypes方法现在已被弃用!

回答by Husam

Here is a SWIFT 2code example for you guys ... It's complicated little bit ,but I hope my comments will help you understand it.

这是给你们的SWIFT 2代码示例......它有点复杂,但我希望我的评论能帮助你理解它。

Define variables

定义变量

var appDidBecomeActiveCount = 0
var userDefaults:NSUserDefaults!

AppDelegate - didFinishLaunchingWithOptions

AppDelegate - didFinishLaunchingWithOptions

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

        userDefaults = NSUserDefaults.standardUserDefaults()
        if userDefaults.valueForKey("FirstLaunche") == nil {
            userDefaults.setBool(true, forKey: "FirstLaunche")
            userDefaults.synchronize()
        }

        // Register for notification
        //iOS 8+
        let settings:UIUserNotificationSettings = UIUserNotificationSettings(forTypes: [UIUserNotificationType.Alert , UIUserNotificationType.Badge ,UIUserNotificationType.Sound], categories: nil)
        UIApplication.sharedApplication().registerUserNotificationSettings(settings)
        UIApplication.sharedApplication().registerForRemoteNotifications()
}

AppDelegate - applicationDidBecomeActive

AppDelegate - applicationDidBecomeActive

func applicationDidBecomeActive(application: UIApplication) {
            //Delay until alert get dismissed and notification type setted in app
            delay(0.5, closure: { () -> () in
                self.checkTheDilemma()
            })
}
//I love this short method <3_<3
func delay(delay:Double, closure:()->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue(), closure)
}

Check action

检查动作

func checkTheDilemma (){
        //Checking if this user turned off push notifications or didn't allow it at all
        let notificationType = UIApplication.sharedApplication().currentUserNotificationSettings()?.types

        if userDefaults.valueForKey("FirstLaunche") as! Bool == true {
            //User now is asked for notification permission because it's app's first launche
            // if appDidBecomeActiveCount == 0 --> Pop up message will appeare
            // if appDidBecomeActiveCount == 1 --> Pop up message dismissed
            // if notificationType?.rawValue == 0 --> Notifications off
            // if notificationType?.rawValue > 0  --> Notifications on
            if notificationType?.rawValue == 0
                && appDidBecomeActiveCount == 1 { //If user disabled notifications from pop up alert
                    // ** User just tapped "Don't allow" btn :\
                    // Do what ever you are here for

                    //Now set FirstLaunche = false
                    userDefaults.setBool(false, forKey: "FirstLaunche")
                    userDefaults.synchronize()
            }
        } else {
            if notificationType?.rawValue == 0
                && appDidBecomeActiveCount == 0 { // This guy is not registered for push notification
                    // ** User disabled notifications in past (because this is not his first launch)
            }
        }
        appDidBecomeActiveCount++
    }

回答by Puneet Sharma

I guess you can have a BOOL variable to check it in your AppDelegate because there seems to be no way other than using external APIs. See this.

我猜你可以有一个 BOOL 变量在你的 AppDelegate 中检查它,因为除了使用外部 API 似乎别无他法。看到这个

AppDelegate.m

// declare a BOOL 
BOOL allow = NO;

- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
allow = YES;
  [self hideLoadingScreen];
}

- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error
{
  allow = YES;
  [self hiedLoadingScreen];
}

Now I guess you can access this BOOL variable to differentiate when Don't allow is pressed or not.

现在我猜你可以访问这个 BOOL 变量来区分何时按下不允许。