简单的警报和徽章IOS 10.2 UserNotifications问题

在写这篇文章之前,我对UserNotification Framework做了大量的研究,在IOS 10中代替了UILocalNotification。我也按照这个教程学习了这个新function的一切: http : //useyourloaf.com/blog/local-notifications-with- ios-10 / 。

今天,我遇到了很多麻烦来实现这样简单的通知,而且由于这是最近的新function,我找不到任何解决scheme(特别是在客观的C中)! 我目前有2个不同的通知,一个警报和一个徽章更新

警报问题

在从iOS 10.1更新我的手机到10.2之前,我在用户closures应用程序时立即触发的Appdelegate发出警报:

-(void)applicationWillTerminate:(UIApplication *)application { NSLog(@"applicationWillTerminate"); // Notification terminate [self registerTerminateNotification]; } // Notification Background terminate -(void) registerTerminateNotification { // the center UNUserNotificationCenter * notifCenter = [UNUserNotificationCenter currentNotificationCenter]; // Content UNMutableNotificationContent *content = [UNMutableNotificationContent new]; content.title = @"Stop"; content.body = @"Application closed"; content.sound = [UNNotificationSound defaultSound]; // Trigger UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:1 repeats:NO]; // Identifier NSString *identifier = @"LocalNotificationTerminate"; // création de la requête UNNotificationRequest *terminateRequest = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger]; // Ajout de la requête au center [notifCenter addNotificationRequest:terminateRequest withCompletionHandler:^(NSError * _Nullable error) { if (error != nil) { NSLog(@"Error %@: %@",identifier,error); } }]; } 

在IOS 10.2之前它工作得很好,当我手动closures应用程序时,出现了一个警报。 但是,因为我更新到IOS 10.2,没有任何理由没有出现,我没有改变任何东西,我看不到什么缺失。

徽章问题

我也尝试过(只在这个时候在IOS 10.2中)来执行我的应用程序图标的徽章,这工作得很好,直到我试图删除它。 这是它的function:

 +(void) incrementBadgeIcon { // only increment if application is in background if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateBackground){ NSLog(@"increment badge"); // notif center UNUserNotificationCenter *notifCenter = [UNUserNotificationCenter currentNotificationCenter]; // Content UNMutableNotificationContent *content = [UNMutableNotificationContent new]; content.badge = [NSNumber numberWithInt:1]; // Trigger UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:1 repeats:NO]; // Identifier NSString *identifier = @"LocalNotificationIncrementBadge"; // request UNNotificationRequest *incrementBadgeRequest = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger]; // Ajout de la requête au center [notifCenter addNotificationRequest:incrementBadgeRequest withCompletionHandler:^(NSError * _Nullable error) { if (error != nil) { NSLog(@"Error %@: %@",identifier,error); } }]; } } 

现在它不会增加徽章号码,因为它的名字应该build议,但它只是设置徽章号码为1.文档说,如果你设置content.badge为0,它会删除它,但这是行不通的。 我试着用其他数字,当我手动将其更改为“2”,“3”等…它会改变,但如果我将它设置为0,它不起作用。

另外,在我之前链接的教程中,提到了几个函数getPendingNotificationRequests:completionHandler:getDeliveredNotificationRequests:completionHandler:。 我注意到,当我调用incrementBadgeIcon后立即调用这些函数,如果content.badge被设置为“1”,“2”等…它出现在待处理的通知列表中。 但是,当我将它设置为0时,它不会出现在任何地方。 我没有错误,在Xcode没有警告,我的应用程序徽章仍然存在。

有谁知道我可以如何解决这两个警报?

预先感谢

PS:我也尝试使用removeAllPendingNotificationRequestsremoveAllDeliveredNotifications都没有成功。

关于警报:

当您的本地通知触发时,您的应用程序仍然处于前台,因此您需要实施一个委托方法,以便通知进行任何操作。 例如,在委托中定义此方法将允许通知显示警报,发出声音并更新徽章:

 func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { completionHandler([.alert,.badge,.sound]) } 

关于徽章:

我观察到,创build一个UNMutableNotificationContent对象并只指定徽章值(作为一个NSNumber对象)适用于除0之外的所有徽章值(即,您不能以这种方式清除徽章)。 我还没有find任何文件为什么0会performance不同于任何其他值,特别是因为.badge属性被定义为一个NSNumber? ,所以框架应该能够区分nil (不变)和0 (清除徽章)。

我已经提出了一个反对这个的雷达 。

作为解决办法,我发现在UNMutableNotificationContent NSNumber(value: 0)的徽章值上设置UNMutableNotificationContent对象的title属性触发。 如果title属性丢失,则不会触发。

添加标题属性仍然不会向用户显示警报( 更新:iOS 11中不再是这种情况! ),所以这是一种悄悄地更新徽章值为0的方法,无需调用UIApplication对象(通过UIApplication.shared.applicationIconBadgeNumber = 0 )。

以下是我的示例项目中的完整代码; 在ViewController代码中有一个MARK,显示插入title属性解决问题的地方:

 // // AppDelegate.swift // userNotificationZeroBadgeTest // // Created by Jeff Vautin on 1/3/17. // Copyright © 2017 Jeff Vautin. All rights reserved. // import UIKit import UserNotifications @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .alert, .sound]) { (success, error) -> Void in print("Badge auth: \(success)") } // For handling Foreground notifications, this needs to be assigned before finishing this method let vc = window?.rootViewController as! ViewController let center = UNUserNotificationCenter.current() center.delegate = vc return true } } // // ViewController.swift // userNotificationZeroBadgeTest // // Created by Jeff Vautin on 1/3/17. // Copyright © 2017 Jeff Vautin. All rights reserved. // import UIKit import UserNotifications class ViewController: UIViewController, UNUserNotificationCenterDelegate { @IBAction func start(_ sender: Any) { // Reset badge directly (this always works) UIApplication.shared.applicationIconBadgeNumber = 0 let center = UNUserNotificationCenter.current() // Schedule badge value of 1 in 5 seconds let notificationBadgeOneContent = UNMutableNotificationContent() notificationBadgeOneContent.badge = NSNumber(value: 1) let notificationBadgeOneTrigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 1*5, repeats: false) let notificationBadgeOneRequest = UNNotificationRequest.init(identifier: "1", content: notificationBadgeOneContent, trigger: notificationBadgeOneTrigger) center.add(notificationBadgeOneRequest) // Schedule badge value of 2 in 10 seconds let notificationBadgeTwoContent = UNMutableNotificationContent() notificationBadgeTwoContent.badge = NSNumber(value: 2) let notificationBadgeTwoTrigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 2*5, repeats: false) let notificationBadgeTwoRequest = UNNotificationRequest.init(identifier: "2", content: notificationBadgeTwoContent, trigger: notificationBadgeTwoTrigger) center.add(notificationBadgeTwoRequest) // Schedule badge value of 3 in 15 seconds let notificationBadgeThreeContent = UNMutableNotificationContent() notificationBadgeThreeContent.badge = NSNumber(value: 3) let notificationBadgeThreeTrigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 3*5, repeats: false) let notificationBadgeThreeRequest = UNNotificationRequest.init(identifier: "3", content: notificationBadgeThreeContent, trigger: notificationBadgeThreeTrigger) center.add(notificationBadgeThreeRequest) // Schedule badge value of 4 in 20 seconds let notificationBadgeFourContent = UNMutableNotificationContent() notificationBadgeFourContent.badge = NSNumber(value: 4) let notificationBadgeFourTrigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 4*5, repeats: false) let notificationBadgeFourRequest = UNNotificationRequest.init(identifier: "4", content: notificationBadgeFourContent, trigger: notificationBadgeFourTrigger) center.add(notificationBadgeFourRequest) // Schedule badge value of 0 in 25 seconds let notificationBadgeZeroContent = UNMutableNotificationContent() // MARK: Uncommenting this line setting title property will cause notification to fire properly. //notificationBadgeZeroContent.title = "Zero!" notificationBadgeZeroContent.badge = NSNumber(value: 0) let notificationBadgeZeroTrigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 5*5, repeats: false) let notificationBadgeZeroRequest = UNNotificationRequest.init(identifier: "0", content: notificationBadgeZeroContent, trigger: notificationBadgeZeroTrigger) center.add(notificationBadgeZeroRequest) } @IBAction func listNotifications(_ sender: Any) { let center = UNUserNotificationCenter.current() center.getDeliveredNotifications() { (notificationArray) -> Void in print("Delivered notifications: \(notificationArray)") } center.getPendingNotificationRequests() { (notificationArray) -> Void in print("Pending notifications: \(notificationArray)") } } // MARK: UNUserNotificationCenterDelegate func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { print("Received notification: \(notification)") completionHandler([.alert,.badge,.sound]) } } 

那么我终于设法使这两个警报function。 就好像在stackoverflow上发布这个问题帮助我打开了关于最近几天持有我的主题(也是非常简单的答案,这是非常可耻的)。

如果有人来这个post,这是我的解决scheme。

警报问题

对于在应用程序closures时应该显示的警报,例如当用户在后台closures应用程序时,代码片段总体上是“正确的”。 重点是,当appDelegate触发applicationWillTerminate:函数时,系统已经开始释放/拆除应用程序的整个内存。 因此,如果您的应用程序有许多视图已加载,并且有多个数据可用,则将通知添加到中心的线程有足够的时间完成任务。 但是,如果应用程序只有less量内存需要处理,通知不会被添加到通知中心的队列中。

 - (void)applicationWillTerminate:(UIApplication *)application { NSLog(@"applicationWillTerminate"); // Notification terminate [Utils closePollenNotification]; // Pause the termination thread [NSThread sleepForTimeInterval:0.1f]; } 

所以在我的情况下,我在创build通知后,在applicationWillTerminate中添加了一个简单的sleep ,这给了足够的时间来注册它。 ( 注意:我不知道这是否是一种好的做法,但对我有用 )。

徽章问题

显然,在对Apple文档有了更好的理解之后,将content.badge设置为0并不会删除以前的徽章集。 它只是告诉通知不更新徽章。 要删除它,我只需要调用sharedApplication函数:

 //Reset badge icon +(void) resetBadgeIcon { NSLog(@"reset badge"); // remove basge [UIApplication sharedApplication].applicationIconBadgeNumber = 0; } 

很简单。

希望这可以帮助别人。

这是OP描述徽章问题(但不是警报问题)的修复。

步骤1:发送badge = negative 1的通知

在你的UNMutableNotificationContent设置content.badge = @-1而不是0.这有两个好处:

  • 不像0,-1实际上清除了徽章(如果你也执行第2步)
  • 不像[UIApplication sharedApplication].applicationIconBadgeNumber = 0它不会清除您的应用程序在通知中心的警报。

第2步:实施UNUserNotificationCenterDelegate

您需要实施UNUserNotificationCenterDelegate并执行以下操作:

  • 在委托中实现willPresentNotification方法,并在正文中调用completionHandler(UNNotificationPresentationOptionBadge) 。 注意:我检查请求ID,并且调用默认的UNNotificationPresentationOptionNone,除非这是专门用来清除徽章的通知。
  • 不要忘记用强指针保留你的委托实例。 通知中心不保留强大的指针。

经过这2次更改后,您可以再次使用本地通知清除徽章。