是否可以在应用未运行时通过本地通知递增应用徽章?
我知道如何设置徽章,但没有找到任何方法来增加此值。
localNotification.applicationIconBadgeNumber = 23;
更新:我找到了一个(远非完美)解决方案。如果用户未打开应用并为每个+1事件添加通知,您可以预测会发生什么。
一个例子:
==>将这些通知放在一个数组中,并在应用程序退出之前设置它们。
但是,我正在寻找比这种解决方法更好的解决方案。
答案 0 :(得分:47)
我发现,实施了&测试了一个“解决方法”(显然)自动递增应用图标的徽章编号,该功能适用于 非重复的本地通知
当多个本地通知被触发时,UILocalNotifications确实不能让iOS“自动”更新/增加徽章编号,并且用户“忽略”它们或者不立即处理它们,因此它们“堆积”起来在通知中心。
此外,“为您的应用添加一些回调方法”无法处理“自动增量”,因为整个通知内容都是由iOS处理的“外部”处理,您的应用甚至不需要运行。
然而,有一些解决方法,这是基于我通过实验发现的知识,因为XCode文档在徽章属性上太模糊。
因此,对于“变通办法”,您的应用必须已为其新创建的每个通知提供正确的递增徽章编号,并在待处理通知之上进行注册。
由于您的应用程序将来无法查看,并且知道您将立即处理哪些事件,以及哪些事件将暂时“暂停”,因此有一些技巧要做:
当您的应用处理通知时(通过点击通知,图标,...),您必须:
此外,当您的应用注册新通知时,它必须首先检查有多少通知待处理,并使用以下内容注册新通知:
badgeNbr = nbrOfPendingNotifications + 1;
查看我的代码,它会更清晰。我测试了这个,它肯定有用:
在“registerLocalNotification”方法中,您应该这样做:
NSUInteger nextBadgeNumber = [[[UIApplication sharedApplication] scheduledLocalNotifications] count] + 1;
localNotification.applicationIconBadgeNumber = nextBadgeNumber;
当你处理通知(appDelegate)时,你应该调用下面的方法,它清除图标上的徽章并重新设置待处理通知的徽章(如果有的话)
请注意,下一个代码适用于“顺序”注册事件。如果您要在待处理事件之间“添加”事件,则必须先对这些事件进行“重新排序”。我没有那么远,但我认为这是可能的。
- (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];
}
}
}
要成为真正的“防弹”,此方法应为“原子”(内核)代码,以防止iOS在执行此方法期间触发通知。我们必须承担这种风险,这种情况很可能会发生。
这是我对Stackoverflow的第一次贡献,所以如果我没有遵循“规则”,你也可以发表评论
答案 1 :(得分:13)
当您的应用程序未运行时,您能够动态设置徽章编号的唯一方法是使用推送通知。您必须在服务器端跟踪更新。
答案 2 :(得分:6)
基于documentation,我相信当您的应用程序未运行时,您无法增加徽章的值。您在安排通知时设置了徽章编号,因此无法递增它。
应用程序负责管理其图标上显示的徽章编号。例如,如果文本消息传递应用程序在收到本地通知后处理所有传入消息,则应通过将UIApplication对象的applicationIconBadgeNumber属性设置为0来删除图标标记。
答案 3 :(得分:2)
在项目代理中添加以下代码。
- (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;
}
}
这对我有用。 : - )
答案 4 :(得分:2)
Whasssaaahhh的回答对我很有帮助。我还需要根据他们的fireDates对通知进行排序。这是Whasssaaahhh的代码,我的代码使用NSArray的委托方法对通知进行排序
- [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的解决方案。
Swift 2.3的解决方案
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中的方法,是您可以自行实现的自我描述方法。
答案 5 :(得分:2)
Whasssaabhhh在Swift 2.1中的回答,包括排序
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)
}
}
}
答案 6 :(得分:1)
从iOS10开始,可以直接在UNMutableNotificationContent上定义徽章编号。
这对我有用:
我正在开发一个基于日期(带有CalendarComponents)添加Notification的应用程序,我的触发器是UNCalendarNotificationTrigger。我的代码很简单:
let content = UNMutableNotificationContent()
content.title = "Title"
content.body = "Your message"
content.sound = .default()
content.badge = NSNumber(value: UIApplication.shared.applicationIconBadgeNumber + 1)
关于content.badge
,医生说:
var badge:NSNumber? {设置}
说明要申请的号码 该应用程序的图标。
使用此属性指定要应用于的数字 通知到达时该应用的图标。如果您的应用不是 授权显示基于徽章的通知,此属性是
指定数字0以删除当前徽章(如果存在)。 指定大于0的数字以显示带有该数字的徽章。 指定nil可使当前标志保持不变。
SDKs iOS 10.0 +,tvOS 10.0 +,watchOS 3.0 +
添加通知后,徽章会自行增加,即使该应用未运行也是如此。您可以使用:
在应用中的任何位置清除徽章编号。UIApplication.shared.applicationIconBadgeNumber = 0
答案 7 :(得分:0)
作为Bionicle解决方案的替代方案,可以使用NSSortDescriptor来处理基于fireDate字段的排序。同样,这个解决方案提供了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];
}
答案 8 :(得分:0)
基于上面的Wassaahbbs和Bionicles答案,对于Swift 3.0,这似乎适用于重复本地通知。我有它设置4个本地通知,每个通知可以独立打开和关闭。
在AppDelegate applicationDidBecomeActive中调用renumberBadgesOfPendingNotifications函数,因此如果用户在收到通知后打开应用程序,则会更新徽章。而且在一个settingsVC中,setNotification函数首先设置通知,如果用户打开或关闭通知,则需要更新徽章。
此外,在applicationDidBecomeActive中,徽章设置为0,UIApplication.shared.applicationIconBadgeNumber = 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. i.e 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. i.e 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)")
}
}
}
答案 9 :(得分:0)
基于上面的Wassaahbbs和Bionicles答案。适用于所有iOS版本的Swift 4.0。在func applicationDidBecomeActive(_ application: UIApplication)
。
func renumberBadgesOfPendingNotifications() {
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().getPendingNotificationRequests { pendingNotificationRequests in
if pendingNotificationRequests.count > 0 {
let notificationRequests = pendingNotificationRequests
.filter { $0.trigger is UNCalendarNotificationTrigger }
.sorted(by: { (r1, r2) -> Bool in
let r1Trigger = r1.trigger as! UNCalendarNotificationTrigger
let r2Trigger = r2.trigger as! UNCalendarNotificationTrigger
let r1Date = r1Trigger.nextTriggerDate()!
let r2Date = r2Trigger.nextTriggerDate()!
return r1Date.compare(r2Date) == .orderedAscending
})
let identifiers = notificationRequests.map { $0.identifier }
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: identifiers)
notificationRequests.enumerated().forEach { (index, request) in
if let trigger = request.trigger {
let content = UNMutableNotificationContent()
content.body = request.content.body
content.sound = .default()
content.badge = (index + 1) as NSNumber
let request = UNNotificationRequest(identifier: request.identifier, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request)
}
}
}
}
} else if let pendingNotifications = UIApplication.shared.scheduledLocalNotifications, pendingNotifications.count > 0 {
let notifications = pendingNotifications
.filter { $0.fireDate != nil }
.sorted(by: { n1, n2 in n1.fireDate!.compare(n2.fireDate!) == .orderedAscending })
notifications.forEach { UIApplication.shared.cancelLocalNotification($0) }
notifications.enumerated().forEach { (index, notification) in
notification.applicationIconBadgeNumber = index + 1
UIApplication.shared.scheduleLocalNotification(notification)
}
}
}
答案 10 :(得分:0)
这是一个棘手的问题。由于 iOS 不会为您跟踪本地通知的徽章编号,因此您需要自行维护每个通知的该编号并及时更新。
为了让它变得更复杂,nextTriggerDate
类中提供的函数 UNTimeIntervalNotificationTrigger
无法正常工作。因此,如果您依靠它对发送通知的待处理通知进行排序,则会出现混乱。
我设法找到的一个实用且简化的解决方案是首先删除所有通知,然后在需要发送新通知时根据您的逻辑重新发送它们。这样就可以保证他们的徽章编号都是正确的。
总而言之,您应该至少在以下条目中执行此类操作:
userNotificationCenter(_:didReceive:withCompletionHandler:)
userNotificationCenter(_:willPresent:withCompletionHandler:)