iPhone: Inkrementieren des Anwendungsabzeichens durch eine lokale Benachrichtigung

Ist es möglich, das Anwendungskennzeichen durch eine lokale Benachrichtigung zu erhöhen, während die App nicht läuft?

Ich weiß, wie man das Badge setzt, habe aber keine Möglichkeit gefunden, diesen Wert zu erhöhen.

localNotification.applicationIconBadgeNumber = 23;

Update: Ich fand eine (weit davon entfernt perfekte) Lösung. Sie können vorhersagen, was passiert, wenn der Benutzer die App nicht öffnet und Benachrichtigungen für jedes +1-Ereignis hinzufügt.

Ein Beispiel:

  • Für Tag 1: Count = 0
  • Für Tag 2: localNotification.applicationIconBadgeNumber = 1;
  • Für Tag 3: localNotification.applicationIconBadgeNumber = 2;
  • Für Tag 4: localNotification.applicationIconBadgeNumber = 3;

==> Fügen Sie diese Benachrichtigungen in ein Array ein und legen Sie sie fest, bevor die Anwendung beendet wird.

Ich suche jedoch nach einer besseren Lösung als diese Problemumgehung.

    Die einzige Möglichkeit, die Ausweisnummer dynamisch festzulegen, wenn Ihre Anwendung nicht ausgeführt wird, sind Push-Benachrichtigungen. Sie müssen die Updates auf der Serverseite verfolgen.

    Ich habe einen “Workaround” gefunden, implementiert und getestet, um die Badge-Nummer des App-Symbols automatisch zu inkrementieren. Dies funktioniert gut mit nicht wiederholten lokalen Benachrichtigungen

    Es ist in der Tat nicht möglich, dass UILocalNotifications iOS ‘automatisch’ die Ausweisnummer aktualisiert / erhöht, wenn mehrere lokale Benachrichtigungen ausgetriggers werden und der Benutzer sie ‘ignoriert’ oder nicht sofort behandelt, sodass sie sich in der Benachrichtigung “anhäufen” Center.

    Auch das Hinzufügen einer Callback-Methode zu Ihrer App kann nicht für die automatische Erhöhung sorgen, da die gesamte Benachrichtigungsfunktion außerhalb Ihrer App von iOS gehandhabt wird. Ihre App muss nicht einmal ausgeführt werden.

    Es gibt jedoch eine Problemumgehung, die auf dem Wissen basiert, das ich beim Experimentieren gefunden habe, weil die XCode-Dokumentation auf der Badge-Eigenschaft zu vage ist.

    • Das Badge ist nur eine “Ganzzahl”, eigentlich eher ein “Dummy-Label”, das Sie der Eigenschaft “applicationIconBadgeNumber” zuweisen, direkt bevor Sie die Benachrichtigung registrieren. Sie können ihm einen beliebigen Wert geben – wenn die Benachrichtigung ausgetriggers wird, fügt iOS diesen Wert dem Badge hinzu, wie immer Sie es zum Zeitpunkt der Registrierung der Benachrichtigung festgelegt haben. Es gibt kein magisches “Auto-Inkrement” oder andere Manipulation durch iOS (vielleicht ist das bei Push-Benachrichtigungen anders, aber das ist hier nicht das Thema). iOS nimmt nur die Nummer (Integer) von der registrierten Benachrichtigung und legt sie in das Badge.

    Für eine “Problemumgehung” muss Ihre App daher für jede Benachrichtigung, die sie neu erstellt und die sich “zusätzlich zu den ausstehenden Benachrichtigungen” anmeldet, bereits die korrekte, fortlaufende Ausweisnummer angeben.

    Da Ihre App in der Zukunft nicht sehen kann und Sie wissen, welche Ereignisse Sie sofort behandeln und welche Sie für eine Weile “ausstehend” lassen, gibt es einen Trick:

    Wenn Benachrichtigungen von Ihrer App bearbeitet werden (durch Antippen der Benachrichtigung (en), Symbol, …), müssen Sie:

    1. Erhalten Sie eine Kopie aller ausstehenden Benachrichtigungen
    2. ‘Nummer’ dieser ausstehenden Benachrichtigungen neu nummerieren
    3. lösche alle ausstehenden Benachrichtigungen
    4. Registrieren Sie die Kopie der Benachrichtigungen erneut mit ihren korrigierten Ausweisnummern

    Wenn Ihre App eine neue Benachrichtigung registriert, muss sie zuerst prüfen, wie viele Benachrichtigungen zuerst ausstehen, und die neue Benachrichtigung registrieren mit:

    badgeNbr = nbrOfPendingNotifications + 1; 

    Wenn ich meinen Code betrachte, wird er klarer. Ich habe das getestet und es funktioniert definitiv:

    In Ihrer ‘registerLocalNotification’ Methode sollten Sie dies tun:

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

    Wenn Sie mit der Benachrichtigung (appDelegate) arbeiten, sollten Sie die folgende Methode aufrufen, die das Badge auf dem Icon löscht und die Badges für ausstehende Benachrichtigungen neu nummeriert (falls vorhanden)

    Beachten Sie, dass der nächste Code für “sequenziell” registrierte Ereignisse funktioniert. Wenn Sie Ereignisse zwischen anstehenden Ereignissen hinzufügen würden, müssen Sie diese Ereignisse zuerst neu sortieren. Ich bin nicht so weit gegangen, aber ich denke, es ist möglich.

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

    Um wirklich ‘kugelsicher’ zu sein, sollte diese Methode ‘atomarer’ (coreel) Code sein, der verhindert, dass das iOS während der Ausführung dieser Methode eine Benachrichtigung austriggers. Wir müssen dieses Risiko eingehen, die Chancen sind sehr gering.

    Dies ist mein erster Beitrag zu Stackoverflow, also kannst du auch einen Kommentar abgeben, wenn ich die “Regeln” nicht befolge

    Aufgrund der Dokumentation glaube ich, dass Sie den Wert des Badge nicht erhöhen können, wenn Ihre Anwendung nicht ausgeführt wird. Sie legen die Ausweisnummer fest, wenn Sie Ihre Benachrichtigung planen, daher ist es nicht möglich, sie zu erhöhen.

    Eine Anwendung ist verantwortlich für die Verwaltung der Ausweisnummer, die auf dem Symbol angezeigt wird. Wenn beispielsweise eine SMS-Anwendung alle eingehenden Nachrichten nach Erhalt einer lokalen Benachrichtigung verarbeitet, sollte sie das Symbolsymbol entfernen, indem Sie die applicationIconBadgeNumber-Eigenschaft des UIApplication-Objekts auf 0 setzen.

    Fügen Sie den folgenden Code in Ihrem Projektdelegaten hinzu.

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

    Das funktioniert für mich. 🙂

    Whasssaabhhhs Antwort in Swift 2.1, mit Sortieren

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

    Whasssaaahhhs Antwort war sehr hilfreich für mich. Ich musste auch die Benachrichtigungen basierend auf ihren FireDates sortieren. Hier ist Whasssaaahhhs Code mit meinem Code, um die Benachrichtigungen mit der Delegate-Methode von NSArray zum Sortieren zu sortieren – [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]; } } } 

    Nach einiger Zeit musste ich dies auf Swift implementieren, musste aber auch lokale Benachrichtigungen wiederholen . Ich habe eine Lösung für Swift gefunden.

    Lösung für 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() } 

    Hinweis: dateByAddingHours, dateByAddingHours, dateByAddingMonths, dateByAddingYears, dateByRemovingSeconds sind Methoden aus einer von mir verwendeten DateExtension und sind selbstbeschreibende Methoden, die Sie selbst implementieren können.

    Als eine Alternative zu der Lösung von Bionicle kann man einen NSSortDescriptor verwenden, um die Sortierung basierend auf dem fireDate-Feld zu handhaben. Wiederum bietet diese Lösung alle Vorteile der ursprünglichen Antwort von Whasssaaahhh, bedeutet aber auch, dass sie Benachrichtigungen in nicht-chronologischer Reihenfolge verarbeiten kann, z. B. das Hinzufügen einer Benachrichtigung in 30 Sekunden und dann in 20 Sekunden. Ich rufe die folgende function beim Hinzufügen einer lokalen Benachrichtigung und beim Zurückkehren zur Anwendung auf.

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

    Basierend auf den obigen Antworten von Wassaahbbs und Bionicles, scheint dies für Swift 3.0 zu funktionieren, lokale Benachrichtigungen zu wiederholen . Ich habe es für die Einstellung von 4 lokalen Benachrichtigungen arbeiten, von denen jede unabhängig voneinander ein- und ausgeschaltet werden kann.

    Die function renumberBadgesOfPendingNotifications wird in AppDelegate applicationDidBecomeActive aufgerufen, sodass die Badges aktualisiert werden, wenn der Benutzer die App nach der Benachrichtigung öffnet. Und auch in einem settingsVC, wo eine setNotification-function die Benachrichtigungen an erster Stelle setzt und falls der Benutzer eine Benachrichtigung ein- oder ausschaltet und somit ein Badge-Update benötigt.

    Außerdem wird das Kennzeichen in applicationDidBecomeActive mit UIApplication.shared.applicationIconBadgeNumber = 0 auf 0 gesetzt.

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

    Basierend auf Wassaahbbs und Bionicles Antworten. Swift 4.0, für alle iOS-Versionen. Rufen Sie diese 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) } } } 

    seit iOS10 ist es möglich, die Ausweisnummer direkt im UNMutellableNotificationContent zu definieren.

    Hier funktioniert was für mich:

    Ich arbeite an einer Anwendung, die Benachrichtigung basierend auf einem Datum (mit CalendarComponents) hinzufügt, mein Auslöser ist UNCalendarNotificationTrigger. Mein Code ist einfach:

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

    Über content.badge sagt das Dokument:

    var Abzeichen: NSNummer? {setzen}

    Beschreibung Die Nummer, die auf das Symbol der App angewendet wird.

    Verwenden Sie diese Eigenschaft, um die Nummer anzugeben, die beim Eintreffen der Benachrichtigung auf das Symbol der App angewendet werden soll. Wenn Ihre App keine Berechtigung zum Anzeigen von badgebasierten Benachrichtigungen besitzt, wird diese Eigenschaft ignoriert.

    Geben Sie die Zahl 0 an, um das aktuelle Abzeichen zu entfernen, falls vorhanden. Geben Sie eine Zahl größer als 0 ein, um ein Abzeichen mit dieser Nummer anzuzeigen. Geben Sie nil an, um das aktuelle Badge unverändert zu lassen.

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

    Das Logo erhöht sich selbst, wenn eine Benachrichtigung hinzugefügt wird, selbst wenn die App nicht ausgeführt wird. Sie können die Badge-Nummer überall in der App löschen mit:

     UIApplication.shared.applicationIconBadgeNumber = 0