35

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

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

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

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

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

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

4

11 回答 11

34

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

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

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

于 2014-04-24T22:17:26.013 回答
6

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

UIRemoteNotificationType notificationTypes = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];

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

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

于 2013-09-27T14:35:00.643 回答
3

这里的一些答案不再相关,或者比应该的更复杂,因为 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.
}
于 2018-07-13T07:04:32.443 回答
2

以下是我在 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
})
于 2017-03-19T01:28:53.617 回答
2

你不能只做以下事情:

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

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

于 2016-01-04T21:47:41.580 回答
1

对于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]) 等等等等

于 2017-12-22T23:02:45.603 回答
1

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

}
于 2019-05-02T11:05:28.937 回答
0

我猜你可以在你的 AppDelegate 中有一个 BOOL 变量来检查它,因为除了使用外部 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];
}

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

于 2013-09-27T14:33:23.183 回答
0

从 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;
  }
}
于 2020-02-29T19:12:01.367 回答
0

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

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

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

于 2017-12-05T05:47:47.773 回答
0

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

定义变量

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++
    }
于 2016-01-26T20:08:17.100 回答