iPhone: incremento del badge dell’applicazione tramite una notifica locale

è ansible incrementare il badge dell’applicazione tramite una notifica locale mentre l’app non è in esecuzione?

So come impostare il badge, ma non ho trovato alcun modo per incrementare questo valore.

localNotification.applicationIconBadgeNumber = 23;

Aggiornamento: ho trovato una soluzione (tutt’altro che perfetta). Puoi prevedere cosa accadrà se l’utente non apre l’app e aggiunge notifiche per ogni evento +1.

Un esempio:

    • Per il giorno 1: Conteggio = 0
    • Per il giorno 2: localNotification.applicationIconBadgeNumber = 1;
    • Per il giorno 3: localNotification.applicationIconBadgeNumber = 2;
    • Per il giorno 4: localNotification.applicationIconBadgeNumber = 3;

    ==> Metti queste notifiche in un array e impostale prima che l’applicazione si chiuda.

    Tuttavia, sto cercando una soluzione migliore di questa soluzione alternativa.

    L’unico modo per impostare dynamicmente il numero del badge quando l’applicazione non è in esecuzione è con le notifiche push. Dovrai tenere traccia degli aggiornamenti sul lato server.

    Ho trovato, implementato e testato una “soluzione” per l’incremento automatico (apparentemente) del numero di badge dell’icona dell’app, che funziona bene con le notifiche locali non ripetitive

    Non è infatti ansible per UILocalNotifications avere iOS ‘automaticamente’ aggiornare / incrementare il numero del badge quando vengono lanciate più notifiche locali, e l’utente ‘ignora’ o non le gestisce immediatamente, quindi si accumulano ‘nella notifica centro.

    Inoltre, “l’aggiunta di un metodo di callback” alla tua app non può tenere conto dell ‘”incremento automatico”, perché tutta la notifica viene gestita “al di fuori” della tua app da iOS, la tua app non ha nemmeno bisogno di essere in esecuzione.

    Tuttavia c’è qualche soluzione alternativa, che si basa sulla conoscenza che ho trovato attraverso la sperimentazione, perché la documentazione di XCode è troppo vaga sulla proprietà del badge.

    • il badge è solo un ‘numero intero’, in realtà più simile a una ‘etichetta fittizia’ che si assegna alla proprietà applicationIconBadgeNumber, proprio prima di registrare la notifica. Puoi dare qualsiasi valore – quando la notifica scatta, iOS aggiungerà quel valore al badge, qualunque sia l’impostazione al momento della registrazione della notifica. Non c’è alcun ‘auto-incremento’ magico o altra manipolazione da parte di iOS (forse è diverso con le notifiche push, ma non è l’argomento qui). iOS prende solo il numero (numero intero) dalla notifica registrata e lo inserisce nel badge.

    Pertanto, per una “soluzione alternativa”, la tua app deve già fornire il numero di badge corretto e incrementale per ogni notifica appena creata e si registra “in aggiunta alle notifiche in sospeso”.

    Dato che la tua app non può guardare in futuro e sapere quali eventi gestirai immediatamente, e quali ti lasceranno in sospeso per un po ‘, c’è un trucco da fare:

    Quando le notifiche vengono gestite dalla tua app (toccando la notifica (s), icona, …), è necessario:

    1. ottenere una copia di tutte le notifiche in sospeso
    2. ‘Rinumerare’ il numero distintivo di queste notifiche in sospeso
    3. elimina tutte le notifiche in sospeso
    4. registrare nuovamente la copia delle notifiche con i loro numeri di badge corretti

    Inoltre, quando l’app registra una nuova notifica, deve verificare quante notifiche sono in sospeso e registrare la nuova notifica con:

    badgeNbr = nbrOfPendingNotifications + 1; 

    Guardando il mio codice, diventerà più chiaro. Ho provato questo, e sicuramente funziona:

    Nel tuo metodo ‘registerLocalNotification’ dovresti fare questo:

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

    Quando gestisci la notifica (appDelegate), devi chiamare il metodo seguente, che cancella il badge sull’icona e rinomina i badge per le notifiche in sospeso (se ce ne sono)

    Si noti che il prossimo codice funziona bene per eventi registrati ‘sequenziali’. Se dovessi “aggiungere” eventi tra quelli in sospeso, dovrai prima riordinare questi eventi. Non sono andato così lontano, ma penso che sia ansible.

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

    Per essere veramente ‘a prova di proiettile’, questo metodo dovrebbe essere codice ‘atomico’ (kernel), impedendo a iOS di lanciare una notifica durante l’esecuzione di questo metodo. Dovremo correre questo rischio qui, è molto probabile che ciò accada.

    Questo è il mio primo contributo a StackOverflow, quindi puoi commentare anche se non sto seguendo le ‘regole’ qui

    Sulla base della documentazione , ritengo che non sia ansible incrementare il valore del badge, quando l’applicazione non è in esecuzione. Si imposta il numero del badge quando si pianifica la notifica, quindi non è ansible incrementarlo.

    Un’applicazione è responsabile della gestione del numero di badge visualizzato sulla sua icona. Ad esempio, se un’applicazione di messaggistica di testo elabora tutti i messaggi in arrivo dopo aver ricevuto una notifica locale, dovrebbe rimuovere il badge icona impostando la proprietà applicationIconBadgeNumber dell’object UIApplication su 0.

    Aggiungi il seguente codice al delegato del tuo progetto.

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

    questo funziona per me. 🙂

    La risposta di Whasssaabhhh in Swift 2.1, con lo smistamento

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

    La risposta di Whasssaaahhh mi è stata di grande aiuto. Avevo anche bisogno di ordinare le notifiche in base ai loro fire date. Ecco il codice di Whasssaaahhh con il mio codice per ordinare le notifiche usando il metodo delegato di NSArray per l’ordinamento – [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]; } } } 

    Dopo qualche tempo, avevo bisogno di implementarlo su Swift ma dovevo anche supportare le notifiche locali . Ho trovato una soluzione su Swift.

    Soluzione per 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() } 

    Nota: dateByAddingHours, dateByAddingHours, dateByAddingMonths, dateByAddingYears, dateByRemovingSeconds sono metodi da un DateExtension che sto utilizzando e sono metodi auto-descrittivi che puoi implementare autonomamente.

    In alternativa alla soluzione di Bionicle, è ansible utilizzare un NSSortDescriptor per gestire l’ordinamento in base al campo fireDate. Ancora una volta questa soluzione offre tutti i vantaggi della risposta originale di Whasssaaahhh, ma significa anche che può gestire le notifiche aggiunte in ordine non cronologico, ad esempio aggiungendo una notifica in 30 secondi, quindi in 20 secondi. Chiamo la funzione qui sotto quando aggiungo una notifica locale e quando ritorna all’applicazione.

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

    Sulla base delle risposte sopra riportate di Wassaahbbs e Bionicles, per Swift 3.0 sembra funzionare per la ripetizione di notifiche locali . Ho funzionato per l’impostazione di 4 notifiche locali, ognuna delle quali può essere triggersta e distriggersta in modo indipendente.

    La funzione renumberBadgesOfPendingNotifications viene chiamata in AppDelegate applicationDidBecomeActive, quindi i badge vengono aggiornati se l’utente apre l’app dopo essere stato avvisato. E anche in un settingsVC in cui una funzione setNotification imposta le notifiche in primo luogo e nel caso in cui l’utente triggers o distriggers una notifica che richiede un aggiornamento del badge.

    Anche il badge è impostato su 0 in applicationDidBecomeActive con 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. 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)") } } } 

    Basato sulle risposte di Wassaahbbs e Bionicles sopra. Swift 4.0, per tutte le versioni iOS. Chiama questa funzione 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) } } } 

    da iOS10 è ansible definire il numero del badge direttamente su UNMutableNotificationContent.

    Ecco cosa funziona per me:

    Sto lavorando a un’applicazione che aggiunge notifiche basate su una data (con CalendarComponents), il mio trigger è UNCalendarNotificationTrigger. Il mio codice è semplicemente:

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

    A proposito di content.badge , il documento dice:

    var distintivo: NSNumber? { preparatevi }

    Descrizione Il numero da applicare all’icona dell’app.

    Utilizzare questa proprietà per specificare il numero da applicare all’icona dell’app quando arriva la notifica. Se la tua app non è autorizzata a visualizzare notifiche basate su badge, questa proprietà viene ignorata.

    Specificare il numero 0 per rimuovere il badge corrente, se presente. Specificare un numero maggiore di 0 per visualizzare un badge con quel numero. Specificare nil per lasciare invariato il badge corrente.

    SDK iOS 10.0+, tvOS 10.0+, watchOS 3.0+

    Il badge aumenta se stesso quando viene aggiunta una notifica anche se l’app non è in esecuzione. Puoi cancellare il numero del badge dove vuoi nell’app con:

     UIApplication.shared.applicationIconBadgeNumber = 0