iPhone: incrémentation du badge d'application via une notification locale

est-il possible d'incrémenter le badge d'application par une notification locale alors que l'application n'est pas en cours d'exécution?

je sais comment régler le badge, mais je n'ai trouvé aucun moyen d'augmenter cette valeur.

localNotification.applicationIconBadgeNumber = 23;

mise à Jour: j'ai trouvé une solution (loin d'être parfaite). Vous pouvez prédire ce qui arrivera si l'utilisateur n'ouvre pas l'application et ajoute des notifications pour chaque +1 événement.

Un exemple:

  • pour le jour 1: Count = 0
  • pour le jour 2: notification locale.applicationIconBadgeNumber = 1;
  • pour le jour 3: notification locale.applicationIconBadgeNumber = 2;
  • pour le jour 4: notification locale.applicationIconBadgeNumber = 3;

==> mettez ces notifications dans un tableau et mettez-les avant la sortie de l'application.

cependant, je cherche une meilleure solution que cette solution de contournement.

27
demandé sur Stefan 2011-05-11 13:31:07

10 réponses

le seul moyen que vous allez être en mesure de définir dynamiquement le numéro de badge lorsque votre application n'est pas en cours d'exécution est avec les notifications push. Vous devrez suivre les mises à jour du côté serveur.

13
répondu visakh7 2011-05-11 10:14:13

j'ai trouvé, implémenté et testé une 'solution de contournement' pour (apparemment) incrémenter automatiquement le numéro de badge de l'icône de l'application, qui fonctionne très bien avec non-répétition des notifications locales

il n'est en effet pas possible pour les notifications Uilocales d'avoir iOS "automatiquement" mettre à jour/incrémenter le numéro de badge lorsque plusieurs notifications locales sont lancées, et que l'utilisateur "les ignore" ou ne les manipule pas immédiatement, de sorte qu'elles "s'empilent" dans le centre de Notification.

aussi 'ajouter une méthode de rappel' à votre application ne peut pas s'occuper de 'l'incrément automatique', parce que toute la chose notification est traitée' en dehors ' de votre application par iOS, votre application n'a même pas besoin d'être en cours d'exécution.

cependant il y a une solution, qui est basée sur les connaissances que j'ai trouvées en expérimentant, parce que la documentation de XCode est trop vague sur la propriété badge.

  • le badge est juste un 'entier', en fait plus comme un 'dummy label' que vous assignez à la propriété applicationIconBadgeNumber, juste avant d'enregistrer la notification. Vous pouvez lui donner valeur lors de la notification des incendies, iOS va ajouter la valeur de l'insigne, ce que vous définissez au moment où vous avez enregistré la notification. Il n'y a pas d'auto-incrément magique ou autre manipulation par iOS (peut-être que c'est différent avec les notifications push, mais ce n'est pas le sujet ici). iOS prend juste le nombre (entier) de la notification enregistrée, et le met dans le badge.

donc pour une ' solution de contournement 'votre application doit déjà fournir le bon numéro de badge incrémentant pour chaque notification qu'elle crée et enregistre'en plus des notifications en attente'.

comme votre application ne peut pas regarder dans le futur, et savoir quels événements vous allez gérer immédiatement, et lesquels vous allez laisser "en attente" pendant un certain temps, il y a un truc à faire :

Lorsque les notifications sont géré par votre application (en cliquant sur la notification(s), d'une icône ...), vous devez :

  1. obtenir une copie de toutes les notifications en attente
  2. 'renuméroter" le numéro de badge de ces notifications en attente
  3. supprimer toutes les notifications en attente
  4. réinscrire la copie des notifications avec leur badge corrigé le nombre d'

aussi, lorsque votre application enregistre une nouvelle notification, elle doit vérifier combien de notifications sont en attente d'abord, et enregistrer la nouvelle notification avec:

badgeNbr = nbrOfPendingNotifications + 1;

en regardant mon code, il deviendra plus clair. J'ai testé, et c'est certainement de travail :

dans votre méthode' registrelocalnotification ' vous devez faire ceci:

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

lorsque vous manipulez la notification (appDelegate), vous devez appeler la méthode ci-dessous, qui efface le badge sur l'icône et renumérote les badges pour les notifications en attente (s'il y en a)

Notez que le code suivant fonctionne très bien pour séquentiel' événements inscrits. Si vous "ajoutez" des événements entre les événements en attente, vous devrez d'abord les "trier". Je ne suis pas allé si loin, mais je pense que c'est 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];
        }
    }
}

pour être vraiment 'bullet proof', cette méthode doit être du code' atomic ' (kernel), empêchant l'iOS de lancer une notification pendant l'exécution de cette méthode. Nous devons prendre ce risque ici, les chances sont très faibles que cela se produise.

C'est mon première contribution à Stackoverflow, donc vous pouvez commenter aussi si Je ne suis pas les 'règles' ici

47
répondu Ronny Webers 2013-03-17 13:35:14

basé sur le documentation, je crois que vous ne pouvez pas augmenter la valeur du badge, quand votre application n'est pas en cours d'exécution. Vous définissez le numéro de badge lorsque vous programmez votre notification, il n'est donc pas possible de l'incrémenter.

Une application est responsable de la gestion du numéro de badge affiché sur son icône. Par exemple, si une application de messagerie texte traite tous les messages entrants après avoir reçu une notification locale, elle devrait supprimer icône badge en définissant la propriété applicationIconBadgeNumber de L'objet UIApplication à 0.

6
répondu Krishnan 2011-05-11 10:07:36

ajouter le code suivant dans votre délégué de projet.

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

cela fonctionne pour moi. : -)

2
répondu Tarang 2014-02-13 13:50:50

Quelle est la réponse de Aaabhhh dans Swift 2.1, avec tri

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)
        }
    }
}
2
répondu Edgardo Agno 2016-03-18 19:25:07

la réponse de Whasssaaahhh m'a été très utile. J'avais aussi besoin de trier les notifications en fonction de leurs dates de feu. Voici le code de Whasssaaahhh avec mon code pour trier les notifications en utilisant la méthode des délégués de NSArray pour trier - [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];
        }
    }
}

après un certain temps, j'ai eu besoin de mettre en œuvre ceci sur Swift mais aussi de supporter répéter les notifications locales. J'ai trouvé une solution sur Swift.

Solution pour 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, datebyadding years, datebyremovingsecondes sont des méthodes d'extension de données que j'utilise et sont des méthodes auto-descriptives que vous pouvez implémenter vous-même.

2
répondu Bionicle 2016-08-08 05:23:42

comme alternative à la solution de Bionicle, on peut utiliser un Nssortdescripteur pour gérer le tri basé sur le champ fireDate. Encore une fois, cette solution offre tous les avantages de la réponse originale de Whassaaahhh, mais elle permet aussi de gérer les notifications ajoutées dans un ordre non chronologique, par exemple en ajoutant une notification en 30 secondes, puis en 20 secondes. J'appelle la fonction ci-dessous lors de l'ajout d'une notification locale, et lors du retour à l'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];
}
0
répondu Steven Craft 2014-10-30 16:59:09

basé sur les réponses de Wassaahbbs et Bionicles ci-dessus, pour Swift 3.0 cela semble fonctionner pour Répéter Les Notifications Locales. Je l'ai fait travailler pour définir 4 notifications locales, dont chacune peut être activée et désactivée indépendamment.

la fonction renumberBadgesOfPendingNotifications est appelée dans AppDelegate applicationDidBecomeActive de sorte que les badges sont mis à jour si l'utilisateur ouvre l'application après avoir été notifié. Et aussi dans un settingsVC où une fonction de notification de sets définit les notifications en premier lieu et dans le cas où l'utilisateur active ou non une notification nécessitant ainsi une mise à jour de badge.

aussi le badge est mis à 0 dans applicationDidBecomeActive avec UIApplication.partager.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)")
        }
    }
}
0
répondu richc 2017-03-22 18:03:25

basé sur les réponses de Wassaahbbs et Bionicles ci-dessus. Swift 4.0, pour toutes les versions iOS. Appelez cette fonction en func applicationDidBecomeActive(_ application: UIApplication).

func renumberBadgesOfPendingNotifications() {
    if #available(iOS 10.0, *) {
        UNUserNotificationCenter.current().getPendingNotificationRequests { pendingNotificationRequests in
            if pendingNotificationRequests.count > 0 {
                let notificationRequests = pendingNotificationRequests
                    .filter { .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 { .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 { .fireDate != nil }
            .sorted(by: { n1, n2 in n1.fireDate!.compare(n2.fireDate!) == .orderedAscending })

        notifications.forEach { UIApplication.shared.cancelLocalNotification() }
        notifications.enumerated().forEach { (index, notification) in
            notification.applicationIconBadgeNumber = index + 1
            UIApplication.shared.scheduleLocalNotification(notification)
        }
    }
}
0
répondu Beniamin Sarkisian 2018-02-11 11:31:07

depuis iOS10 il est possible de définir le numéro de badge directement sur le contenu de la notification non valide.

Ici ce qui fonctionne pour moi :

je travaille sur une application qui ajoute une Notification basée sur une Date (avec des composants de calendrier), mon déclencheur est UNCalendarNotificationTrigger. Mon code est tout simplement:

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