29

アプリが実行されていないときに、ローカル通知を介してアプリケーションバッジをインクリメントすることは可能ですか?

バッジの設定方法は知っていますが、この値を増やす方法が見つかりません。

localNotification.applicationIconBadgeNumber = 23;

更新:(完璧にはほど遠い)解決策を見つけました。ユーザーがアプリを開かず、+ 1イベントごとに通知を追加しない場合、何が起こるかを予測できます。

例:

  • 1日目:カウント= 0
  • 2日目:localNotification.applicationIconBadgeNumber = 1;
  • 3日目:localNotification.applicationIconBadgeNumber = 2;
  • 4日目:localNotification.applicationIconBadgeNumber = 3;

==>これらの通知を配列に入れて、アプリケーションが終了する前に設定します。

ただし、この回避策よりも優れた解決策を探しています。

4

11 に答える 11

50

アプリアイコンのバッジ番号を(明らかに)自動インクリメントするための「回避策」を見つけ、実装し、テストしました。これは、繰り返されないローカル通知で正常に機能します。

実際、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への私の最初の貢献なので、ここで「ルール」に従わない場合にもコメントできます

于 2013-03-17T13:35:14.800 に答える
14

アプリケーションが実行されていないときにバッジ番号を動的に設定できるようにする唯一の方法は、プッシュ通知を使用することです。サーバー側で更新を追跡する必要があります。

于 2011-05-11T10:14:13.327 に答える
6

ドキュメントに基づくと、アプリケーションが実行されていない場合、バッジの値を増やすことはできないと思います。通知をスケジュールするときにバッジ番号を設定するため、バッジ番号を増やすことはできません。

アプリケーションは、そのアイコンに表示されるバッジ番号を管理する責任があります。たとえば、テキストメッセージングアプリケーションがローカル通知を受信した後にすべての着信メッセージを処理する場合、UIApplicationオブジェクトのapplicationIconBadgeNumberプロパティを0に設定して、アイコンバッジを削除する必要があります。

于 2011-05-11T10:07:36.307 に答える
2

プロジェクトデリゲートに次のコードを追加します。

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    NSLog(@"%s",__FUNCTION__);

    NSArray *arrayOfLocalNotifications = [[UIApplication sharedApplication] scheduledLocalNotifications] ;

    for (UILocalNotification *localNotification in arrayOfLocalNotifications) {
        NSLog(@"the notification: %@", localNotification);
        localNotification.applicationIconBadgeNumber= application.applicationIconBadgeNumber+1;
    }
}

これは私のために働きます。:-)

于 2014-02-13T13:50:50.937 に答える
2

Whasssaaahhhの答えは私にとって非常に役に立ちました。また、fireDatesに基づいて通知を並べ替える必要がありました。これは、ソート用のNSArrayのデリゲートメソッドを使用して通知をソートするための私のコードを含むWhasssaaahhhのコードです-[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の解決策を考え出しました。

Swift2.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のメソッドであり、独自に実装できる自己記述型のメソッドです。

于 2014-03-11T03:13:54.393 に答える
2

Swift 2.1でのWhasssaabhhhの回答、並​​べ替え

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)
        }
    }
}
于 2016-03-18T19:25:07.267 に答える
2

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?{getset}

説明アプリのアイコンに適用する番号。

このプロパティを使用して、通知が到着したときにアプリのアイコンに適用する番号を指定します。アプリがバッジベースの通知を表示することを許可されていない場合、このプロパティは無視されます。

現在のバッジが存在する場合は削除するには、数字の0を指定します。0より大きい数値を指定すると、その数値のバッジが表示されます。現在のバッジを変更しない場合は、nilを指定します。

SDK iOS 10.0以降、tvOS 10.0以降、watchOS3.0以降

アプリが実行されていない場合でも、通知が追加されるとバッジが自動的に増加します。アプリ内のどこでもバッジ番号をクリアできます:

UIApplication.shared.applicationIconBadgeNumber = 0
于 2018-06-21T21:20:57.453 に答える
0

Bionicleのソリューションの代わりに、NSSortDescriptorを使用して、fireDateフィールドに基づく並べ替えを処理できます。この場合も、このソリューションはWhasssaaahhhの元の回答のすべての利点を提供しますが、通知を時系列ではない順序で追加できることも意味します。たとえば、30秒後に通知を追加し、次に20秒後に通知を追加します。ローカル通知を追加するとき、およびアプリケーションに戻るときに、以下の関数を呼び出します。

// When we add/remove local notifications, if we call this function, it will ensure each notification
// will have an ascending badge number specified.
- (void)renumberBadgesOfPendingNotifications
{
    // Clear the badge on the icon
    [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];

    // First get a copy of all pending notifications (unfortunately you cannot 'modify' a pending notification)
    NSMutableArray * pendingNotifications = [[[UIApplication sharedApplication] scheduledLocalNotifications] mutableCopy];

    // Sorted by fire date.
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"fireDate" ascending:TRUE];
    [pendingNotifications sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
    [sortDescriptor release];

    // if there are any pending notifications -> adjust their badge number
    if (pendingNotifications.count != 0)
    {
        // clear all pending notifications
        [[UIApplication sharedApplication] cancelAllLocalNotifications];

        // the for loop will 'restore' the pending notifications, but with corrected badge numbers
        // note : a more advanced method could 'sort' the notifications first !!!
        NSUInteger badgeNbr = 1;

        for (UILocalNotification *notification in pendingNotifications)
        {
            // modify the badgeNumber
            notification.applicationIconBadgeNumber = badgeNbr++;

            // schedule 'again'
            [[UIApplication sharedApplication] scheduleLocalNotification:notification];
        }
    }

    // Release our copy.
    [pendingNotifications release];
}
于 2014-10-30T16:59:09.437 に答える
0

上記のWassaahbbsとBioniclesの回答に基づくと、Swift 3.0の場合、これはローカル通知の繰り返しに対して機能しているようです。4つのローカル通知を設定するために機能しています。各通知は個別にオンとオフを切り替えることができます。

renumberBadgesOfPendingNotifications関数はAppDelegateapplicationDidBecomeActiveで呼び出されるため、ユーザーが通知を受けた後にアプリを開くとバッジが更新されます。また、setNotification関数が最初に通知を設定するsettingsVCでも、ユーザーが通知をオンまたはオフにした場合にバッジを更新する必要があります。

また、バッジは、UIApplication.shared.applicationIconBadgeNumber=0のapplicationDidBecomeActiveで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)")
        }
    }
}
于 2017-03-22T18:03:25.377 に答える
0

上記の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)
        }
    }
}
于 2018-02-11T11:14:56.080 に答える
0

それはトリッキーな問題です。iOSはローカル通知のバッジ番号を追跡しないため、各通知のバッジ番号を維持し、タイムリーに更新するのはあなた次第です。

さらに複雑にするためnextTriggerDateに、クラスで提供されている関数UNTimeIntervalNotificationTriggerは正しく機能しません。したがって、これに依存して保留中の通知を送信側の通知と一緒に注文すると、混乱が生じます。

私が見つけた実用的で単純化された解決策の1つは、最初にすべての通知を削除してから、新しい通知を送信する必要があるときはいつでも、ロジックに基づいて通知を再送信することです。そうすれば、それらのバッジ番号はすべて正しいことが保証されます。

結論として、少なくともこれらのエントリでそのようなアクションを実行する必要があります。

  1. userNotificationCenter(_:didReceive:withCompletionHandler:)
  2. userNotificationCenter(_:willPresent:withCompletionHandler:)
  3. 新しい通知を送信する場所。
  4. アプリのバッジ番号をゼロに設定した場所。
于 2021-06-26T04:10:50.787 に答える