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

发布于 2024-11-06 05:58:03 字数 565 浏览 1 评论 0 原文

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

我知道如何设置徽章,但还没有找到任何方法来增加这个值。

localNotification.applicationIconBadgeNumber = 23;

更新:我找到了一个(远非完美的)解决方案。您可以预测如果用户不打开应用程序并为每个 +1 事件添加通知会发生什么。

示例:

  • 对于第 1 天:Count = 0
  • 对于第 2 天:localNotification.applicationIconBadgeNumber = 1;
  • 第 3 天:localNotification.applicationIconBadgeNumber = 2;
  • 第 4 天:localNotification.applicationIconBadgeNumber = 3;

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

但是,我正在寻找比此解决方法更好的解决方案。

is it possible to increment the application badge through a local notification while the app is not running?

I know how to set the badge, but haven't found any way to increment this value.

localNotification.applicationIconBadgeNumber = 23;

Update: I found a (far from being perfect) solution. You can predict what will happen, if the user doesn't open the app and add notifications for every +1 event.

An example:

  • For day 1: Count = 0
  • For day 2: localNotification.applicationIconBadgeNumber = 1;
  • For day 3: localNotification.applicationIconBadgeNumber = 2;
  • For day 4: localNotification.applicationIconBadgeNumber = 3;

==> Put these notifications in an array and set them before the application exits.

However, I'm searching for a better solution than this workaround.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

这样的小城市 2024-11-13 05:58:04

我发现、实施并实施了测试了一种(显然)自动递增应用程序图标徽章编号的“解决方法”,该方法可以很好地处理非重复本地通知

UILocalNotifications 确实不可能有当多个本地通知被触发时,iOS 会“自动”更新/增加徽章编号,并且用户“忽略”它们或没有立即处理它们,因此它们会“堆积”在通知中心。

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

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

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

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

由于您的应用程序无法展望未来,并且知道哪些事件将立即处理,哪些事件将“待处理”一段时间,因此需要采取一些技巧:

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

  1. 获取所有待处理通知的副本
  2. “重新编号”这些待处理通知的徽章编号
  3. 删除所有待处理通知
  4. 使用更正的徽章重新注册通知副本
    另外

,当您的应用程序注册新通知时,它必须首先检查有多少通知待处理,然后使用以下命令注册新通知:

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 的第一个贡献,因此如果我不遵守此处的“规则”,您也可以发表评论

I've found, implemented & tested a 'workaround' for (apparantly) auto-incrementing the app icon's badge number, that works fine with non-repeating local notifications

It is indeed not possible for UILocalNotifications to have iOS 'automatically' update/increment the badge number when multiple local notifications are fired, and the user 'ignores' them or doesn't handle them immediately, so they 'pile up' in the Notification centre.

Also 'adding some callback method' to your app cannot take care of the 'auto increment', because the whole notification thing is handled 'outside' of your app by iOS, your app doesn't even need to be running.

However there is some workaround, that is based on the knowledge which I found through experimenting, because the XCode documentation is too vague on the badge property.

  • the badge is just an 'integer', actually more like a 'dummy label' that you assign to the applicationIconBadgeNumber property, right before you register the notification. You can give it any value - when the notification fires, iOS will add that value to the badge, whatever you set it to at the time you registered the notification. There is no magic 'auto-increment' or other manipulation by iOS (maybe that is different with push notifications, but that's not the subject here). iOS just takes the number (integer) from the registered notification, and puts it in the badge.

So for a 'workaround' your app must already provide the correct, incrementing badge number for each notification it newly creates and registers 'on top of the pending notifications'.

Since your app cannot look in the future, and know which events you'll handle immediately, and which ones you'll leave 'pending' for a while, there's some trick to do :

When notifications are handled by your app (by tapping on the notification(s), icon, ...), you have to :

  1. get a copy of all pending notifications
  2. 'renumber' the badge number of these pending notifications
  3. delete all pending notifications
  4. re-register the copy of the notifications with their corrected badge
    numbers again

Also, when your app registers a new notification, it has to check how many notifications are pending first, and register the new notification with with :

badgeNbr = nbrOfPendingNotifications + 1;

Looking at my code, it will get clearer. I tested this, and it's definitely working :

In your 'registerLocalNotification' method you should do this :

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

When you handle the notification (appDelegate), you should call the method below, which clears the badge on the icon and renumbers the badges for pending notifications (if there are any)

Note that the next code works fine for 'sequential' registered events. If you would 'add' events in between pending ones, you'll have to 're-sort' these events first. I didn't go that far, but I think it's possible.

- (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];
        }
    }
}

To be truly 'bullet proof', this method should be 'atomic' (kernel) code, preventing the iOS from firing of a notification during the execution of this method. We'll have to take this risk here, chances are very small this will happen.

This is my first contribution to Stackoverflow, so you can comment also if I'm not following the 'rules' here

腻橙味 2024-11-13 05:58:04

当应用程序未运行时,您能够动态设置徽章编号的唯一方法是使用推送通知。您必须跟踪服务器端的更新。

The only way you're going to be able to dynamically set the badge number when your application isn't running is with push notifications. You'll have to track the updates on the server side.

罪歌 2024-11-13 05:58:04

基于 文档 ,我相信当您的应用程序未运行时,您无法增加徽章的值。您在安排通知时设置徽章编号,因此无法增加它。

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

Based on the documentation , I believe you can not increment the value of the badge, when your application is not running. You set the badge number when you schedule your notification, so it is not possible to increment it.

An application is responsible for managing the badge number displayed on its icon. For example, if a text-messaging application processes all incoming messages after receiving a local notification, it should remove the icon badge by setting the applicationIconBadgeNumber property of the UIApplication object to 0.

残花月 2024-11-13 05:58:04

从 iOS10 开始,可以直接在 UNMutableNotificationContent 上定义徽章编号。

这对我有用:

我正在开发一个基于日期添加通知的应用程序(使用 CalendarComponents),我的触发器是 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 徽章:NSNumber? { 准备好 }

描述 要申请的号码
应用程序的图标。

使用此属性指定要应用的号码
通知到达时应用程序的图标。如果您的应用程序不是
授权显示基于徽章的通知,此属性是
忽略。

指定数字 0 以删除当前徽章(如果存在)。
指定大于 0 的数字以显示具有该数字的徽章。
指定 nil 可使当前徽章保持不变。

SDK iOS 10.0+、tvOS
10.0+、watchOS 3.0+

即使应用程序未运行,添加通知时徽章也会自行递增。您可以在应用程序中的任何位置清除徽章编号:

UIApplication.shared.applicationIconBadgeNumber = 0

since iOS10 it's possible to define the badge number directly on the UNMutableNotificationContent.

Here what's works for me :

I'm working on an application which adds Notification based on a Date (with CalendarComponents), my trigger is UNCalendarNotificationTrigger. My code is simply:

let content = UNMutableNotificationContent()
        content.title = "Title"
        content.body = "Your message"
        content.sound = .default()
        content.badge = NSNumber(value: UIApplication.shared.applicationIconBadgeNumber + 1)

About content.badge, the doc says :

var badge: NSNumber? { get set }

Description The number to apply to
the app’s icon.

Use this property to specify the number to apply to
the app’s icon when the notification arrives. If your app is not
authorized to display badge-based notifications, this property is
ignored.

Specify the number 0 to remove the current badge, if present.
Specify a number greater than 0 to display a badge with that number.
Specify nil to leave the current badge unchanged.

SDKs iOS 10.0+, tvOS
10.0+, watchOS 3.0+

The badge increments itself when a notification is added even if the app is not running. You can clear the badge number wherever you want in the app with :

UIApplication.shared.applicationIconBadgeNumber = 0
朦胧时间 2024-11-13 05:58:04

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

- (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;
    }
}

这对我有用。 :-)

Add following code in your project delegate.

- (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;
    }
}

this works for me. :-)

余生再见 2024-11-13 05:58:04

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

Whasssaabhhh's answer in Swift 2.1, with 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)
        }
    }
}
等你爱我 2024-11-13 05:58:04

Whasssaaahhh 的回答对我很有帮助。我还需要根据通知的触发日期对通知进行排序。这是 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 中的方法,并且是您可以自己实现的自描述方法。

Whasssaaahhh's answer was very helpful to me. I also needed to sort the notifications based on their fireDates. Here's Whasssaaahhh's code with my code to sort the notifications using the NSArray's delegate method for 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];
        }
    }
}

After sometime, I needed to implement this on Swift but also needed to support repeating local notifications. I've come up with a solution on Swift.

Solution for 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()
    }

Note: dateByAddingHours, dateByAddingHours, dateByAddingMonths, dateByAddingYears, dateByRemovingSeconds are methods from a DateExtension I'm using and are self-descriptive methods you can implement on your own.

我做我的改变 2024-11-13 05:58:04

作为 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];
}

As an alternative to Bionicle's solution, one can use a NSSortDescriptor to handle sorting based on the fireDate field. Again this solution provides all the benefits of Whasssaaahhh's original answer, but also means it can handle notifications being added in non chronological order, e.g. adding a notification in 30 seconds time, then in 20 seconds time. I call the below function when adding a local notification, and when returning to the application.

// 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];
}
痕至 2024-11-13 05:58:04

根据上面的 Wassaahbbs 和 Bionicles 答案,对于 Swift 3.0,这似乎适用于重复本地通知。我让它可以设置 4 个本地通知,每个通知都可以独立打开和关闭。

renumberBadgesOfPendingNotifications 函数在 AppDelegate applicationDidBecomeActive 中调用,因此如果用户在收到通知后打开应用程序,徽章就会更新。并且在 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)")
        }
    }
}

Based on Wassaahbbs and Bionicles answers above, for Swift 3.0 this seems to be working for Repeating Local Notifications. I have it working for setting 4 local notifications, each of which can be turned on and off independently.

The renumberBadgesOfPendingNotifications function is called in AppDelegate applicationDidBecomeActive so badges are updated if user opens the app after being notified. And also in a settingsVC where a setNotification function sets the notifications in the first place and In case user turns a notification on or off thus needing a badge update.

Also the badge is set to 0 in applicationDidBecomeActive with 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)")
        }
    }
}
給妳壹絲溫柔 2024-11-13 05:58:04

基于 Wassaahbbs 和 Bionicles 的上述答案。 Swift 4.0,适用于所有 iOS 版本。在 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)
        }
    }
}

Based on Wassaahbbs and Bionicles answers above. Swift 4.0, for all iOS versions. Call this function in 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)
        }
    }
}
薔薇婲 2024-11-13 05:58:04

这是一个棘手的问题。由于 iOS 不会为您跟踪本地通知的徽章编号,因此您需要维护每个通知的徽章编号并及时更新它们。

更复杂的是,UNTimeIntervalNotificationTrigger类中提供的函数nextTriggerDate无法正常工作。因此,如果您依靠它来对待处理的通知与发送的通知进行排序,则会出现混乱。

我设法找到的一个实用且简化的解决方案是首先删除所有通知,然后每当您需要发送新通知时根据您的逻辑重新发送它们。这样就保证了他们的徽章号码都是正确的。

总之,您应该至少在以下条目中执行此类操作:

  1. userNotificationCenter(_:didReceive:withCompletionHandler:)
  2. userNotificationCenter(_:willPresent:withCompletionHandler:)
  3. 您发送的位置一个新的通知。
  4. 您将应用程序的徽章编号设置为零的位置。

It's a tricky problem. Since iOS does not track the badge number of the local notifications for you, it is up to you to maintain that number of each notification and update them timely.

And just to make it more complicated, the function nextTriggerDate which provided in the UNTimeIntervalNotificationTrigger class doesn't work properly. So if you rely on that to order the pending notifications with the sending one, there will be chaos.

One practical and simplified solution I've managed to found is to first remove all notification then re-send them based on your logics whenever you need to send a new notification. That way the badge number of them are guaranteed to be all correct.

In conclusion, you should do such actions at at least these entries:

  1. userNotificationCenter(_:didReceive:withCompletionHandler:)
  2. userNotificationCenter(_:willPresent:withCompletionHandler:)
  3. where you send out a new notification.
  4. Where you set your app's badgenumber to zero.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文