检测iOS8中“允许通知”是否开启

41

我正在尝试检测 iOS 8 中应用的本地通知设置。

对于 UIUserNotificationSettings,它返回给我数字 7,因为我已经开启了所有提醒标记、声音和警报。

在设置中,我关闭了“允许通知”,但应用仍然返回数字 7 来表示 UIUserNotificationSettings(提醒标记、声音和警报已打开)。有没有方法可以检测“允许通知”是否开启或关闭?

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

    NSLog(@"---notificationSettings.types %d" , notificationSettings.types );
    if(notificationSettings.types!=7){
        UIAlertView * alert =[[UIAlertView alloc ] initWithTitle:@"Please turn on Notification"
                                                         message:@"Go to Settings > Notifications > App.\n Switch on Sound, Badge & Alert"
                                                        delegate:self
                                               cancelButtonTitle:@"Ok"
                                               otherButtonTitles: nil];
        [alert show];
    }
}

7是什么?你是随机想出了一个神奇的数字吗? - pronebird
@Andy:如果你将Badge、Sound和Alert组合起来,7就是UIUserNotificationType的rawValue。 - mathz
@mathz 是的,我明白了,我想知道为什么 OP 要使用数字而不是枚举来寻找麻烦。 - pronebird
当然。rawValue 当然可以改变。我使用类似 types == UIUserNotificationType.None(在 Swift 中)的语句来检查用户是否禁用了推送。 - mathz
1
我在iOS 8(iPhone 4S)上遇到了同样的问题,如果我接受通知权限,我从UIUserNotificationSettings中得到7个,但是当我删除并重新安装应用程序后,在应用程序的通知设置中我看到我没有启用通知权限,但是在代码中我再次收到了7个UIUserNotificationSettings,你找到解决方法了吗?(在iOS 9上正常工作) - magofdl
显示剩余2条评论
11个回答

22

enabledRemoteNotificationTypes方法自iOS8起已被弃用。

要在iOS8中检查远程通知状态,可以调用

[[UIApplication sharedApplication] isRegisteredForRemoteNotifications];

如果用户在设置中禁用了通知,它将返回NO。有关isRegisteredForRemoteNotifications的文档。

或者您可以检索所有当前的通知设置:

[[UIApplication sharedApplication] currentUserNotificationSettings];

currentUserNotificationSettingsDocumentation


1
我认为这不正确...我的测试表明,如果用户启用了“允许通知”,但是将警报类型设置为无,并在通知中禁用了“声音”,那么“isRegisteredForRemoteNotifications”将返回false。 - Daniel Baughman
3
当应用仅使用本地通知时,似乎isRegisteredForRemoteNotifications始终为NO。 - Maxim Kholyavkin
14
@Ponf,我认为您的描述略有不准确。您说“[[UIApplication sharedApplication] isRegisteredForRemoteNotifications];如果用户在设置中禁用通知,则会返回NO”,但文档说:“此方法仅反映调用registerForRemoteNotifications方法时开始的远程注册过程的成功完成”。这意味着如果用户在提示时允许通知,然后进入设置并禁用它们,isRegisteredForRemoteNotifications仍将返回true。 - Jonathon Horsman
7
嗨@Ponf,这正是我想的,但不幸的是,它没有反映出用户允许通知后再进入设置并禁用它们所做的更改。我已经测试过了,尽管设置被禁用,但isRegisteredForRemoteNotifications返回true。 - Jonathon Horsman
1
isRegisteredForRemoteNotifications 在我激活通知后总是返回 true。即使我从设置中禁用它,它仍然给我一个 yes。 :( @JonathonHorsman 你能想出一些解决方案吗? - anoop4real
显示剩余10条评论

14

Swift 3+

let isRegisteredForLocalNotifications = UIApplication.shared.currentUserNotificationSettings?.types.contains(UIUserNotificationType.alert) ?? false

Swift 2.3

let isRegisteredForLocalNotifications = UIApplication.sharedApplication().currentUserNotificationSettings()?.types.contains(UIUserNotificationType.Alert) ?? false

1
这里有一个特殊情况:如果你禁用了声音和标记,保持警报样式为无,同时启用“在通知中心显示”和“在锁定屏幕显示”,那么你将得到currentUserNotificationSettings?.types.rawValue == 0;但事实上,你仍然可以在通知中心接收通知并打开它。这真的很奇怪。 - DawnSong

13

如果我的回答/评论发布在了错误的位置,我表示抱歉。我对iOS编程非常陌生,之前也从未在 Stack Overflow 上发过帖子。我认为这应该是一条评论,但是因为没有50个赞,我无法进行评论。同时,如果我的解释有些基础,那也请多包涵,毕竟我还很新手 :)

另外,我想测试用户是否在初始请求后更改了应用程序所允许/要求的通知设置。经过尝试阅读 Apple 的文档(Apple 的作者要么比我聪明得多,要么是文档故意晦涩难懂),以及一些测试后,我测试了该值的数值。

[[UIApplication sharedApplication] currentUserNotificationSettings].hash.

我认为这会返回一个三位比特哈希值,其中第一位用于横幅通知,第二位用于声音通知,第三位用于警报通知。

所以...

000 = 0 = no notifications.
001 = 1 = only banner,
010 = 2 = only sound,
011 = 3 = sound and banner, no alert
100 = 4 = only alert notifications
and so on until,
111 = 7 = all notifications on.

如果在设置应用中关闭了允许通知,这也会显示为0。

希望这可以帮到你。

4
虽然这些可能是正确的标志值,但使用NSObject的哈希属性是不正确的,因为它不是对象的“值” - 它只是用于比较目的的哈希。没有人应该依赖它来获取值。相反,应该使用currentUserNotificationSettings的值,它是一个带有正确属性以进行查询的NSUserNotificationSettings对象。 - lupinglade

11

要在iOS8和iOS9中检查远程通知的状态,您需要调用:

// Obj-C
[[UIApplication sharedApplication] isRegisteredForRemoteNotifications]; 
// Swift
UIApplication.sharedApplication().isRegisteredForRemoteNotifications

如果您的应用程序可以接收远程通知,则返回true。但是,接收远程通知并不意味着它也会将其显示给用户。

registerForRemoteNotifications()请求在几乎所有情况下都会成功,并调用AppDelegate的didRegisterForRemoteNotificationsWithDeviceToken方法,该方法提供设备令牌。然后,应用程序可以接收静默的远程通知,这些通知不会显示给用户。例如,当接收到此类通知时,您可以在应用程序中触发后台处理过程。请参见文档

要不仅接收而且将远程通知显示给用户,您需要使用以下代码请求权限:

// Swift
let notificatonSettings = UIUserNotificationSettings(forTypes: [.Badge, .Alert, .Sound], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(notificatonSettings)

这将向用户显示一个对话框,在其中他们可以允许或拒绝您的请求。无论他们做出什么决定,应用程序仍将能够接收远程通知。

如果用户允许,则会调用AppDelegate的didRegisterForRemoteNotificationsWithDeviceToken

要检查用户是允许还是拒绝了您的请求,或者实际上在iOS设置中稍后更改了通知权限,您需要调用:

// Obj-C
[[UIApplication sharedApplication] currentUserNotificationSettings];
// Swift
UIApplication.sharedApplication().currentUserNotificationSettings()   

请参阅文档


嗨,Manuel - 有没有一种方法可以跟踪通知设置是否已更改,而不仅仅是当前状态?也许我应该设置自己的NSUserDefaults标志? - richc
1
自iOS 9.3起,您只能检查当前的身份验证状态。将当前通知身份验证状态存储在NSUserDefaults中并根据其进行检查以检测权限更改绝对是一种良好的实用选项,我已经看到了许多次。不幸的是,目前还没有等效的位置权限更改触发函数“locationManager(manager:CLLocationManager,didChangeAuthorizationStatus status:CLAuthorizationStatus)”,该函数可在用户在iOS设置中更改权限时触发。希望iOS 10能够挽救局面。 - Manuel
嗨,Manuel - 这个相当冗长的检查是我放在 applicationDidBecomeActive 中的。它运行良好。 let currentStatus = application.currentUserNotificationSettings() let currentStatusBool = currentStatus!.types.contains(.Badge) let previousStatus = NSUserDefaults.standardUserDefaults().boolForKey("userNotificationSettings") if currentStatusBool == true && previousStatus == true { // .Badge 是一个有效的选项,并且先前的状态为 true,因此没有任何变化,所以什么也不做 } 对于其他 3 种组合也是如此。再次感谢。 - richc

9

当用户第一次启动应用程序时,这应该是有效的,用户将会收到一个对话框,检查用户是否拒绝或允许通知,请使用以下代码:

-(void)application:(UIApplication *)application didRegisterUserNotificationSettings:   (UIUserNotificationSettings *)notificationSettings
{
    if (notificationSettings.types) {
        NSLog(@"user allowed notifications");
        [[UIApplication sharedApplication] registerForRemoteNotifications];
    }else{
        NSLog(@"user did not allow notifications");
        // show alert here
    }
}

在连续启动时,请使用以下方法:

[[UIApplication sharedApplication] isRegisteredForRemoteNotifications];

这正是我所需要的。如果之前拒绝接收通知,没有人会说它仍然会到达这一点,并且永远不会调用didFailToRegsiterForRemoteNotifications。我只需要这个脚本,这样我就可以调用自己的函数了。 - James
在使用这种方法时要小心,用户可能会在设置中将通知设置为无。每次使用用户通知时,请务必检查允许的设置。 - newshorts

2

Swift 2

在AppDelegate中:

func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings) {

    if (notificationSettings.types == .None){ // User did NOT allowed notifications


    }else{ // User did allowed notifications


    }

}

从任何其他视图控制器:

    if UIApplication.sharedApplication().currentUserNotificationSettings()!.types.contains(.None){

    }else{

    }

首先,不要使用感叹号。其次,types.contains(.none) 总是返回 true。 - DawnSong

2
UIUserNotificationType notificationType = [[[UIApplication sharedApplication] currentUserNotificationSettings] types];

if(notificationType == UIRemoteNotificationTypeNone)
        {

            NSLog(@"OFF");
        }
        else{

            NSLog(@"ON");
        }

适用于我


1
你可以控制 UIApplication.sharedApplication().currentUserNotificationSettings() 的哈希值。
if(UIApplication.instancesRespondToSelector(Selector("registerUserNotificationSettings:"))){
        if(UIApplication.sharedApplication().currentUserNotificationSettings().hashValue == 0){
            pushNotificationStatus = "false"
        } else {
            pushNotificationStatus = "true"
        }
}

1
不要使用 .hash 属性(没有 hashValue 属性)。使用 currentUserNotificationSettings 值本身。它是一个 UIUserNotificationSettings 对象,您可以与 types 属性进行比较。 - lupinglade

0

使用.isRegisteredForRemoteNotifications()方法并不起作用(虽然它应该)。不过,当通知被禁用或其中一种类型不存在时,以下代码会起作用。

func notificationsAreOk() -> Bool {
    let wishedTypes = UIUserNotificationType.Badge |
        UIUserNotificationType.Alert |
        UIUserNotificationType.Sound;
    let application = UIApplication.sharedApplication()
    let settings = application.currentUserNotificationSettings()
    if settings == nil {
        return false
    }
    if settings.types != wishedTypes {
        return false
    }
    return true
}

编辑:经过一些测试,当通知被禁用时,它并不总是有效。我正在考虑发送一个测试通知来确定它们何时起作用。


嘿,这很棒,但我无法弄清楚如何在通知中关闭标记或声音而不触发通知(我只关心警报)。有什么想法吗? - jammyman34
settings.types 包含实际配置,告诉您哪些功能是启用的。我猜(但不确定)您可以使用类似 setting.types & UIUserNotificationType.Badge 的东西进行测试,然后创建一些 updatedWishedTypes,然后可以使用它来配置通知:UIUserNotificationSettings(forTypes: updatedWishedTypes, categories: nil);。希望这有所帮助。 - AsTeR
如果启用了后台刷新,即使用户在设置中禁用了可视推送通知,isRegisteredForRemoteNotifications也将为true。 - lupinglade

0

看看这个。代码经过测试验证。

- (BOOL)isUserNotificationAllowed {
    UIUserNotificationType types = [[UIApplication sharedApplication] currentUserNotificationSettings].types;
    if(types & UIUserNotificationTypeBadge || types & UIUserNotificationTypeSound || types & UIUserNotificationTypeAlert){
        return YES;
    }
    else {
        return NO;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接