iPhone:通过本地通知递增应用程序徽章

应用程序未运行时,是否可以通过本地通知增加应用程序徽章?

我知道如何设置徽章,但没有find任何增加这个值的方法。

localNotification.applicationIconBadgeNumber = 23;

更新:我发现(远非完美)解决scheme。 您可以预测会发生什么,如果用户没有打开应用程序并为每个+1事件添加通知。

一个例子:

  • 第1天:计数= 0
  • 第二天:localNotification.applicationIconBadgeNumber = 1;
  • 第3天:localNotification.applicationIconBadgeNumber = 2;
  • 第4天:localNotification.applicationIconBadgeNumber = 3;

==>把这些通知放在一个数组中,并在应用程序退出之前设置它们。

不过,我正在寻找比这个解决方法更好的解决scheme。

当您的应用程序不运行时,您将能够dynamic设置徽章号码的唯一方法是使用推送通知。 你将不得不跟踪服务器端的更新。

我已经发现,实施并testing了一个“解决方法”,用于(显然)自动递增应用程序图标的徽章号码,可以正常使用非重复的本地通知

UILocalNotifications确实不可能在多个本地通知被触发时“自动”更新/增加徽章号码,并且用户'忽略'它们或不立即处理它们,所以它们在通知中'堆积'中央。

此外,向应用程序添加一些callback方法无法关注“自动增量”,因为整个通知事件是由iOS应用程序的“外部”处理的,您的应用程序甚至不需要运行。

然而,有一些解决方法是基于我通过实验发现的知识,因为XCode文档在徽章属性上过于模糊。

  • 徽章只是一个“整数”,实际上更像是在注册通知之前分配给applicationIconBadgeNumber属性的“虚拟标签”。 您可以给它任何值 – 当通知触发时,iOS会将该值添加到徽章,无论您在注册通知时设置了哪个值。 iOS没有神奇的“自动增量”或其他操作(也许这与推送通知不同,但这不是主题)。 iOS只是从注册的通知中获取数字(整数),并将其放入徽章中。

因此,对于“解决方法”,您的应用程序必须已经为其新创build的每个通知提供了正确的递增徽章编号,并在“待处理的通知之上”进行注册。

由于您的应用程序无法预测未来,并且知道您将立即处理哪些事件,以及哪些事件将会暂时搁置一段时间,因此需要执行一些技巧:

当您的应用程序处理通知(通过点击通知,图标…),您必须:

  1. 获得所有未决通知的副本
  2. 重新编号这些待处理通知的徽章号码
  3. 删除所有未决的通知
  4. 再次重新注册通知副本及其更正的徽章号码

此外,当您的应用程序注册一个新的通知时,它必须先检查有多less通知处于挂起状态,然后用以下方式注册新的通知:

badgeNbr = nbrOfPendingNotifications + 1; 

看我的代码,它会变得更清晰。 我testing了这个,它肯定是工作:

在你的“registerLocalNotification”方法中,你应该这样做:

 NSUInteger nextBadgeNumber = [[[UIApplication sharedApplication] scheduledLocalNotifications] count] + 1; localNotification.applicationIconBadgeNumber = nextBadgeNumber; 

当您处理通知(appDelegate)时,您应该调用下面的方法,该方法清除图标上的徽章,并重新标记待处理通知的徽章(如果有的话)

请注意,下一个代码适用于“顺序”注册事件。 如果您要在两个待处理的事件之间添加事件,则必须先对这些事件进行“重新sorting”。 我没有去那么远,但我认为这是可能的。

 - (void)renumberBadgesOfPendingNotifications { // clear the badge on the icon [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0]; // first get a copy of all pending notifications (unfortunately you cannot 'modify' a pending notification) NSArray *pendingNotifications = [[UIApplication sharedApplication] scheduledLocalNotifications]; // if there are any pending notifications -> adjust their badge number if (pendingNotifications.count != 0) { // clear all pending notifications [[UIApplication sharedApplication] cancelAllLocalNotifications]; // the for loop will 'restore' the pending notifications, but with corrected badge numbers // note : a more advanced method could 'sort' the notifications first !!! NSUInteger badgeNbr = 1; for (UILocalNotification *notification in pendingNotifications) { // modify the badgeNumber notification.applicationIconBadgeNumber = badgeNbr++; // schedule 'again' [[UIApplication sharedApplication] scheduleLocalNotification:notification]; } } } 

要真正地“防弹”,这个方法应该是“primefaces”(内核)代码,防止iOS在执行这个方法的时候触发通知。 我们将不得不在这里冒这个风险,很可能会发生这样的事情。

这是我对Stackoverflow的第一个贡献,所以如果我不遵循这里的“规则”,你也可以发表评论

基于文档 ,我相信当你的应用程序没有运行时,你不能增加徽章的价值。 您在安排通知时设置了徽章号码,因此无法递增。

应用程序负责pipe理其图标上显示的徽章号码。 例如,如果文本消息传递应用程序在收到本地通知后处理所有传入消息,则应通过将UIApplication对象的applicationIconBadgeNumber属性设置为0来删除图标徽章。

在您的项目委托中添加以下代码。

 - (void)applicationDidEnterBackground:(UIApplication *)application { NSLog(@"%s",__FUNCTION__); NSArray *arrayOfLocalNotifications = [[UIApplication sharedApplication] scheduledLocalNotifications] ; for (UILocalNotification *localNotification in arrayOfLocalNotifications) { NSLog(@"the notification: %@", localNotification); localNotification.applicationIconBadgeNumber= application.applicationIconBadgeNumber+1; } } 

这对我有用。 🙂

Whasssaabhhh的答案在Swift 2.1中进行了sorting

 func renumberBadgesOfPendingNotifications() { let app = UIApplication.sharedApplication() let pendingNotifications = app.scheduledLocalNotifications // clear the badge on the icon app.applicationIconBadgeNumber = 0 // first get a copy of all pending notifications (unfortunately you cannot 'modify' a pending notification) // if there are any pending notifications -> adjust their badge number if let pendings = pendingNotifications where pendings.count > 0 { // sorted by fire date. let notifications = pendings.sort({ p1, p2 in p1.fireDate!.compare(p2.fireDate!) == .OrderedAscending }) // clear all pending notifications app.cancelAllLocalNotifications() // the for loop will 'restore' the pending notifications, but with corrected badge numbers var badgeNumber = 1 for n in notifications { // modify the badgeNumber n.applicationIconBadgeNumber = badgeNumber++ // schedule 'again' app.scheduleLocalNotification(n) } } } 

Whasssaaahhh的回答对我很有帮助。 我还需要根据他们的fireDatessorting通知。 这里是Whasssaaahhh的代码与我的代码sorting通知使用NSArray的委托方法进行sorting – [NSArray sortedArrayUsingComparator:^(id obj1, id obj2) {}];

 - (void)renumberBadgesOfPendingNotifications { // clear the badge on the icon [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0]; // first get a copy of all pending notifications (unfortunately you cannot 'modify' a pending notification) // Sort the pending notifications first by their fireDate NSArray *pendingNotifications = [[[UIApplication sharedApplication] scheduledLocalNotifications] sortedArrayUsingComparator:^(id obj1, id obj2) { if ([obj1 isKindOfClass:[UILocalNotification class]] && [obj2 isKindOfClass:[UILocalNotification class]]) { UILocalNotification *notif1 = (UILocalNotification *)obj1; UILocalNotification *notif2 = (UILocalNotification *)obj2; return [notif1.fireDate compare:notif2.fireDate]; } return NSOrderedSame; }]; // if there are any pending notifications -> adjust their badge number if (pendingNotifications.count != 0) { // clear all pending notifications [[UIApplication sharedApplication] cancelAllLocalNotifications]; // the for loop will 'restore' the pending notifications, but with corrected badge numbers // note : a more advanced method could 'sort' the notifications first !!! NSUInteger badgeNbr = 1; for (UILocalNotification *notification in pendingNotifications) { // modify the badgeNumber notification.applicationIconBadgeNumber = badgeNbr++; // schedule 'again' [[UIApplication sharedApplication] scheduleLocalNotification:notification]; } } } 

过了一段时间,我需要在Swift上实现这一点,但也需要支持重复本地通知 。 我在Swift上提出了一个解决scheme。

Swift 2.3的解决scheme

 func renumberBadgesOfPendingNotifications() { let app = UIApplication.sharedApplication() let pendingNotifications = app.scheduledLocalNotifications // clear the badge on the icon app.applicationIconBadgeNumber = 0 // first get a copy of all pending notifications (unfortunately you cannot 'modify' a pending notification) // if there are any pending notifications -> adjust their badge number if let pendings = pendingNotifications where pendings.count > 0 { // Reassign firedate. var notifications = pendings var i = 0 for notif in notifications { if notif.fireDate?.compare(NSDate()) == NSComparisonResult.OrderedAscending && notif.repeatInterval.rawValue == NSCalendarUnit.init(rawValue:0).rawValue { // Skip notification scheduled earlier than current date time // and if it is has NO REPEAT INTERVAL } else { notif.fireDate = getFireDate(notif) } i+=1 } // sorted by fire date. notifications = pendings.sort({ p1, p2 in p1.fireDate!.compare(p2.fireDate!) == .OrderedAscending }) // clear all pending notifications app.cancelAllLocalNotifications() // the for loop will 'restore' the pending notifications, but with corrected badge numbers var badgeNumber: Int = 1 for n in notifications { // modify the badgeNumber n.applicationIconBadgeNumber = badgeNumber badgeNumber+=1 // schedule 'again' app.scheduleLocalNotification(n) } } } private func getFireDate(notification:UILocalNotification?) -> NSDate? { if notification == nil { return nil } let currentDate: NSDate = NSDate().dateByRemovingSeconds() let originalDate: NSDate = notification!.fireDate! var fireDate: NSDate? = originalDate if originalDate.compare(currentDate) == NSComparisonResult.OrderedAscending || originalDate.compare(currentDate) == NSComparisonResult.OrderedSame { let currentDateTimeInterval = currentDate.timeIntervalSinceReferenceDate let originalDateTimeInterval = originalDate.timeIntervalSinceReferenceDate var frequency:NSTimeInterval = 0 switch notification?.repeatInterval { case NSCalendarUnit.Hour?: frequency = currentDate.dateByAddingHours(1).timeIntervalSinceDate(currentDate) print(frequency) break case NSCalendarUnit.Day?: frequency = currentDate.dateByAddingDays(1).timeIntervalSinceDate(currentDate) print(frequency) break case NSCalendarUnit.WeekOfYear?: frequency = currentDate.dateByAddingDays(7).timeIntervalSinceDate(currentDate) print(frequency) break case NSCalendarUnit.Month?: frequency = currentDate.dateByAddingMonths(1).timeIntervalSinceDate(currentDate) print(frequency) break case NSCalendarUnit.Year?: frequency = currentDate.dateByAddingYears(1).timeIntervalSinceDate(currentDate) print(frequency) break default: originalDate } let timeIntervalDiff = (((currentDateTimeInterval - originalDateTimeInterval) / frequency) + frequency) + originalDateTimeInterval fireDate = NSDate(timeIntervalSinceReferenceDate: timeIntervalDiff) } return fireDate?.dateByRemovingSeconds() } 

注意:dateByAddingHours,dateByAddingHours,dateByAddingMonths,dateByAddingYears,dateByRemovingSeconds是我正在使用的DateExtension中的方法,并且是您自己可以实现的自描述方法。

作为Bionicle解决scheme的替代scheme,可以使用NSSortDescriptor来处理基于fireDate字段的sorting。 这个解决scheme再次提供了Whasssaaahhh最初的答案的所有好处,但也意味着它可以处理非按时间顺序添加的通知,例如在30秒内添加通知,然后在20秒内添加通知。 添加本地通知时以及返回到应用程序时,我调用下面的函数。

 // When we add/remove local notifications, if we call this function, it will ensure each notification // will have an ascending badge number specified. - (void)renumberBadgesOfPendingNotifications { // Clear the badge on the icon [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0]; // First get a copy of all pending notifications (unfortunately you cannot 'modify' a pending notification) NSMutableArray * pendingNotifications = [[[UIApplication sharedApplication] scheduledLocalNotifications] mutableCopy]; // Sorted by fire date. NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"fireDate" ascending:TRUE]; [pendingNotifications sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]]; [sortDescriptor release]; // if there are any pending notifications -> adjust their badge number if (pendingNotifications.count != 0) { // clear all pending notifications [[UIApplication sharedApplication] cancelAllLocalNotifications]; // the for loop will 'restore' the pending notifications, but with corrected badge numbers // note : a more advanced method could 'sort' the notifications first !!! NSUInteger badgeNbr = 1; for (UILocalNotification *notification in pendingNotifications) { // modify the badgeNumber notification.applicationIconBadgeNumber = badgeNbr++; // schedule 'again' [[UIApplication sharedApplication] scheduleLocalNotification:notification]; } } // Release our copy. [pendingNotifications release]; } 

根据上面的Wassaahbbs和Bionicles的回答,对于Swift 3.0,这似乎是重复本地通知的工作 。 我有它的工作设置4个本地通知,每个可以独立开启和closures。

在AppDelegate applicationDidBecomeActive中调用renumberBadgesOfPendingNotifications函数,以便在用户在收到通知后打开应用程序时更新徽章。 而且在一个setNotification函数首先设置通知的情况下以及在用户打开或closures通知的情况下,因此需要徽章更新。

此外,在UIApplication.shared.applicationIconBadgeNumber = 0的applicationDidBecomeActive中,徽章设置为0。

 func renumberBadgesOfPendingNotifications() { // first get a copy of all pending notifications (unfortunately you cannot 'modify' a pending notification) let pendingNotifications = UIApplication.shared.scheduledLocalNotifications print("AppDel there are \(pendingNotifications?.count) pending notifs now") // if there are any pending notifications -> adjust their badge number if var pendings = pendingNotifications, pendings.count > 0 { // sort into earlier and later pendings var notifications = pendings var earlierNotifs = [UILocalNotification]() var laterNotifs = [UILocalNotification]() for pending in pendings { // Skip notification scheduled earlier than current date time if pending.fireDate?.compare(NSDate() as Date) == ComparisonResult.orderedAscending { // and use this if it has NO REPEAT INTERVAL && notif.repeatInterval.rawValue == NSCalendar.Unit.init(rawValue:0).rawValue { // track earlier and later pendings earlierNotifs.append(pending) } else { laterNotifs.append(pending) } } print("AppDel there are \(earlierNotifs.count) earlier notifications") print("AppDel there are \(laterNotifs.count) later notifications") // change the badge on the notifications due later pendings = laterNotifs // sorted by fireDate. notifications = pendings.sorted(by: { p1, p2 in p1.fireDate!.compare(p2.fireDate!) == .orderedAscending }) // clear all pending notifications. ie the laterNotifs for pending in pendings { UIApplication.shared.cancelLocalNotification(pending) } // the for loop will 'restore' the pending notifications, but with corrected badge numbers var laterBadgeNumber = 0 for n in notifications { // modify the badgeNumber laterBadgeNumber += 1 n.applicationIconBadgeNumber = laterBadgeNumber // schedule 'again' UIApplication.shared.scheduleLocalNotification(n) print("AppDel later notif scheduled with badgenumber \(n.applicationIconBadgeNumber)") } // change the badge on the notifications due earlier pendings = earlierNotifs // sorted by fireDate. notifications = pendings.sorted(by: { p1, p2 in p1.fireDate!.compare(p2.fireDate!) == .orderedAscending }) // clear all pending notifications. ie the laterNotifs for pending in pendings { UIApplication.shared.cancelLocalNotification(pending) } // the for loop will 'restore' the pending notifications, but with corrected badge numbers var earlierBadgeNumber = laterBadgeNumber for n in notifications { // modify the badgeNumber earlierBadgeNumber += 1 n.applicationIconBadgeNumber = earlierBadgeNumber // schedule 'again' UIApplication.shared.scheduleLocalNotification(n) print("AppDel earlier notif scheduled with badgenumber \(n.applicationIconBadgeNumber)") } } }