【问题标题】:Callback Method if user declines Push Notification Prompt?如果用户拒绝推送通知提示,回调方法?
【发布时间】:2013-10-03 21:19:37
【问题描述】:

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

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

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

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

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

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

【问题讨论】:

  • in iOS 10 如果你使用UserNotifications framework 那么你可以通过回调来判断用户是否点击了yes/no。见here

标签: ios objective-c push-notification apple-push-notifications


【解决方案1】:

我想您可以在 AppDelegate 中使用 BOOL 变量来检查它,因为除了使用外部 API 之外似乎别无他法。见this

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];
}

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

【讨论】:

  • 嗯但是什么时候访问 BOOL?如果用户不允许推送通知,似乎不会触发回调。
  • 你想消失加载屏幕对吧?如果我猜对了,那么您会在启动时向用户显示加载屏幕。然后应用程序要求允许推送通知。如果允许则显示不同的屏幕,如果不允许则显示不同的屏幕。对吗?
  • 好吧,允许和禁止是同一个屏幕,但基本上你是对的。只要用户按下是或否,当前的加载屏幕就会消失。
  • 太糟糕了。幸运的是,我们同意在弹出窗口现在出现时不显示加载屏幕。
  • 对你有好处。享受你的一天!
【解决方案2】:

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

UIRemoteNotificationType notificationTypes = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];

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

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

【讨论】:

  • 我的问题是 didRegisterForRemoteNotificationsWithDeviceToken 和 didFailToRegisterForRemoteNotificationsWithError 永远不会被调用,如果用户在提示上点击 no。
  • 赞成,因为尽管事实上这并没有直接回答原始问题,但它确实提到了一些非常有用的东西,甚至在registerForRemoteNotificationTypes的文档中都没有提到!
  • iOS 8 SDK 中的 API 已更改。在 iOS >= 8.0 上,您必须检查 [[UIApplication sharedApplication] currentUserNotificationSettings].categories
【解决方案3】:

在 iOS 7 中,当系统的推送通知提示出现时,应用程序变为非活动状态并触发 UIApplicationWillResignActiveNotification。同样,当用户响应提示(按是或否)时,应用程序再次变为活动状态并触发 UIApplicationDidBecomeActiveNotification。

因此您可以收听此通知,然后隐藏您的加载屏幕。

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

【讨论】:

  • 我可以确认这种行为在 iOS 10 中仍然存在
  • 我可以确认这种行为在 iOS 12 中仍然存在
  • 我可以确认这种行为在 iOS 14 beta 2 中仍然存在
【解决方案4】:

您不能只执行以下操作:

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

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

【讨论】:

    【解决方案5】:

    这里有一个SWIFT 2代码示例给你们……有点复杂,但我希望我的cmets能帮助你理解它。

    定义变量

    var appDidBecomeActiveCount = 0
    var userDefaults:NSUserDefaults!
    

    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

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

    检查操作

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

    【讨论】:

      【解决方案6】:

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

      这是一个非常大的 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)
      }
      

      用法:

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

      【讨论】:

        【解决方案7】:

        您可以通过检查notificationSettings.types 来检测用户是否取消了在调用registerForRemoteNotificationTypes 后触发的didRegisterUserNotificationSettings 方法中的通知提示。

        如果您请求了多项设置,但 notificationSettings.types == UIUserNotificationTypeNone 表示该用户已取消提示。

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

        【讨论】:

          【解决方案8】:

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

          不幸的是,NotificationCenter 没有预设通知名称,因此您必须设置和扩展(见末尾)或使用原始值(SO 对此有更多信息)。

          在 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])
          }
          

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

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

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

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

          然后我可以这样使用:

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

          【讨论】:

            【解决方案9】:

            这里的一些答案不再相关,或者比它应该的更复杂,因为 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.
            }
            

            【讨论】:

              【解决方案10】:

              2019 年 5 月 2 日

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

                  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.")
                      }
                  }
              
              }
              

              【讨论】:

                【解决方案11】:

                从 iOS 13 开始使用 C# Xamarin 的好方法

                我把它放在我提示并检查授权状态的页面上的计时器中

                可能会得到实际的回调,但这种方式对我有用

                using System.Timers;
                
                Timer notificationsPermissionTimer = new Timer();
                
                public override void ViewDidLoad()
                {
                  SetupNotificationsPermissionTimer();
                  base.ViewDidLoad();
                }
                
                public override void ViewWillDisappear(bool animated)
                {
                  this.notificationsPermissionTimer.Elapsed -= NotificationsPermissionTimer_Elapsed;
                  base.ViewWillDisappear(animated);
                }
                private void SetUpNotificationsPermissionTimer()
                {
                  this.notificationsPermissionTimer = new Timer();
                  this.notificationsPermissionTimer.Interval = 500;
                  this.notificationsPermissionTimer.Start();
                  this.notificationsPermissionTimer.Elapsed += NotificationsPermissionTimer_Elapsed;
                }
                
                private void NotificationsPermissionTimer_Elapsed(object sender, ElapsedEventArgs e)
                {
                  Task.Run(CheckNotificationsAuthorizationStatus);
                }
                
                private async Task CheckNotificationsAuthorizationStatus()
                {
                  var userNotificationCenter = await UNUserNotificationCenter.Current.GetNotificationSettingsAsync();
                  switch(userNotificationCenter.AuthorizationStatus)
                  {
                    case UNAuthorizationStatus.Authorized:
                      // Do Something
                      break;
                    case UNAuthorizationStatus.Denied:
                      // Do Something
                      break;
                    case UNAuthorizationStatus.NotDetermined:
                      // Do Nothing
                      break;
                    case UNAuthorizationStatus.Provisional:
                      break;
                  }
                }
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2015-01-14
                  • 2021-10-11
                  • 2013-11-30
                  • 2011-09-28
                  • 1970-01-01
                  • 2015-01-25
                  • 1970-01-01
                  相关资源
                  最近更新 更多