Значок системы уведомлений пользователя не увеличивается

Я использую UserNotification framework в своем приложении и отправляю локальные уведомления (а не push-уведомления), и я хочу установить значок на количество полученных уведомлений, поэтому я установил количество полученных уведомлений по умолчанию для пользователя, затем я попытался присвоить значение значку, чтобы получить номер значка, но номер значка не будет увеличиваться. Это мой код ниже

Чтобы установить значение полученного уведомления

center.getDeliveredNotifications { notification in

    UserDefaults.standard.set(notification.count, forKey: Constants.NOTIFICATION_COUNT)
    print("notification.count \(notification.count)")
    print(".count noti \(UserDefaults.standard.integer(forKey: Constants.NOTIFICATION_COUNT))")

}

Это точно печатает количество полученных уведомлений, и когда я решил установить его на свой значок, он показывает только 1

content.badge = NSNumber(value: UserDefaults.standard.integer(forKey: Constants.NOTIFICATION_COUNT))

Я понятия не имею, почему значение не увеличивается каждый раз. Любая помощь будет оценена по достоинству.

Или если есть возможность всегда обновлять значок в любом месте приложения.


person King    schedule 14.09.2018    source источник
comment
Я не вижу ни одной строки, указывающей на то, что значение увеличилось или изменилось.   -  person El Tomato    schedule 14.09.2018
comment
Что мне не хватает?   -  person King    schedule 14.09.2018
comment
Вы упускаете какое-либо объяснение того, почему, по вашему мнению, значение будет увеличиваться. Вы не увеличиваете его, так чего же вы ожидаете, что он увеличится?   -  person matt    schedule 16.09.2018
comment
Как бы мне получить значок, чтобы показать количество уведомлений тогда. в документах ничего не нашел   -  person King    schedule 16.09.2018
comment
Какой значок вы пытаетесь установить? Значок значка приложения, который вы видите на главном экране iOS?   -  person gmarintes    schedule 16.09.2018
comment
Куда ты звонишь center.getDeliveredNotifications?   -  person ielyamani    schedule 16.09.2018
comment
Я не знаю, где это назвать. он вызывается в Viewcontroller, где запускается объект, который устанавливает уведомление   -  person King    schedule 16.09.2018
comment
@King Я удалил свой голос против ответа Carpsen90 и проголосовал за него. Но на данный момент это очень сложный ответ.   -  person Honey    schedule 20.09.2018
comment
Я наградил ближайший ответ, но ни один ответ не решил проблему   -  person King    schedule 23.09.2018
comment
@King Можете ли вы прокомментировать принятый ответ и объяснить, почему он не решил вашу проблему?   -  person Honey    schedule 28.09.2018
comment
Таким образом, уведомление работает, чтобы отображать вещи, на которые пользователь должен обратить внимание в этот конкретный момент времени. Поэтому, если я установлю напоминание на 16:00 завтра и другое на 17:00 на следующей неделе, как только завтра будет 16:00, я должен получить уведомление, что сейчас 16:00, и это уведомление, но написанный код на самом деле показывает общее количество установленных уведомлений, даже если уведомление должно быть запущено в следующем году, и оно устанавливает значок приложения в качестве этого числа. Вместо того, чтобы показывать текущие уведомления, доставленные.   -  person King    schedule 28.09.2018


Ответы (2)


Отправьте локальные уведомления следующим образом:

func sendNotification(title: String, subtitle: String, body: String, timeInterval: TimeInterval) {
    let center = UNUserNotificationCenter.current()
    center.getPendingNotificationRequests(completionHandler: { pendingNotificationRequests in

        //Use the main thread since we want to access UIApplication.shared.applicationIconBadgeNumber
        DispatchQueue.main.sync {

            //Create the new content
            let content = UNMutableNotificationContent()
            content.title = title
            content.subtitle = subtitle
            content.body = body

            //Let's store the firing date of this notification in content.userInfo
            let firingDate = Date().timeIntervalSince1970 + timeInterval
            content.userInfo = ["timeInterval": firingDate]

            //get the count of pending notification that will be fired earlier than this one
            let earlierNotificationsCount: Int = pendingNotificationRequests.filter { request in

                let userInfo = request.content.userInfo
                if let time = userInfo["timeInterval"] as? Double {
                    if time < firingDate {
                        return true
                    } else {

                        //Here we update the notofication that have been created earlier, BUT have a later firing date
                        let newContent: UNMutableNotificationContent = request.content.mutableCopy() as! UNMutableNotificationContent
                        newContent.badge = (Int(truncating: request.content.badge ?? 0) + 1) as NSNumber
                        let newRequest: UNNotificationRequest =
                            UNNotificationRequest(identifier: request.identifier,
                                                  content: newContent,
                                                  trigger: request.trigger)
                        center.add(newRequest, withCompletionHandler: { (error) in
                            // Handle error
                        })
                        return false
                    }
                }
                return false
            }.count

            //Set the badge
            content.badge =  NSNumber(integerLiteral: UIApplication.shared.applicationIconBadgeNumber + earlierNotificationsCount + 1)
            let trigger = UNTimeIntervalNotificationTrigger(timeInterval: timeInterval,
                                                            repeats: false)

            let requestIdentifier = UUID().uuidString  //You probably want to save these request identifiers if you want to remove the corresponding notifications later
            let request = UNNotificationRequest(identifier: requestIdentifier,
                                                content: content, trigger: trigger)

            center.add(request, withCompletionHandler: { (error) in
                // Handle error
            })
        }
    })
}

(Возможно, вам потребуется сохранить идентификаторы запросов (либо в пользовательских настройках по умолчанию, либо в основных данных, если вы хотите их обновить, или даже отменить их через removePendingNotificationRequests(withIdentifiers:))

Вы можете вызвать вышеуказанную функцию следующим образом:

sendNotification(title: "Meeting Reminder",
                 subtitle: "Staff Meeting in 20 minutes",
                 body: "Don't forget to bring coffee.",
                 timeInterval: 10)

Объявите свой контроллер представления как UNUserNotificationCenterDelegate:

class ViewController: UIViewController, UNUserNotificationCenterDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        UNUserNotificationCenter.current().delegate = self
    }
    //...
}

И для обработки взаимодействия с уведомлением обновите значок приложения и значок предстоящих уведомлений:

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {

    //UI updates are done in the main thread
    DispatchQueue.main.async {
        UIApplication.shared.applicationIconBadgeNumber -= 1
    }

    let center = UNUserNotificationCenter.current()
    center.getPendingNotificationRequests(completionHandler: {requests in
        //Update only the notifications that have userInfo["timeInterval"] set
        let newRequests: [UNNotificationRequest] =
            requests
                .filter{ rq in
                    return rq.content.userInfo["timeInterval"] is Double?
                }
                .map { request in
                    let newContent: UNMutableNotificationContent = request.content.mutableCopy() as! UNMutableNotificationContent
                    newContent.badge = (Int(truncating: request.content.badge ?? 0) - 1) as NSNumber
                    let newRequest: UNNotificationRequest =
                        UNNotificationRequest(identifier: request.identifier,
                                              content: newContent,
                                              trigger: request.trigger)
                    return newRequest
        }
        newRequests.forEach { center.add($0, withCompletionHandler: { (error) in
            // Handle error
        })
        }
    })
    completionHandler()
}

Это обновляет значок приложения, уменьшая его при взаимодействии с уведомлением, например, касании. Кроме того, он обновляет значок содержимого ожидающих уведомлений. Добавление запроса на уведомление с тем же идентификатором просто обновляет ожидающее уведомление.

Чтобы получать уведомления на переднем плане и увеличивать значок значка приложения, если с уведомлением не взаимодействуют, выполните следующее:

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    DispatchQueue.main.async {
        UIApplication.shared.applicationIconBadgeNumber += 1
    }
    completionHandler([.alert, .sound])
}

Вот несколько гифок:

  • : получение локальных уведомлений увеличивает значок приложения. В то время как взаимодействие с уведомлением уменьшает значок приложения.

  • : получение локальных уведомлений при завершении работы приложения (я использовал триггер timeInterval из 15 секунд в этом).

  • 3: получение уведомления на переднем плане увеличивает значок приложения, если пользователь не взаимодействует с Это.

Полный класс, используемый в моем тестовом проекте, выглядит так:

import UIKit
import UserNotifications

class ViewController: UIViewController, UNUserNotificationCenterDelegate {
    var bit = true
    @IBAction func send(_ sender: UIButton) {
        let time: TimeInterval = bit ? 8 : 4
        bit.toggle()
        sendNotification(title: "Meeting Reminder",
                         subtitle: "Staff Meeting in 20 minutes",
                         body: "Don't forget to bring coffee.",
                         timeInterval: time)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        UNUserNotificationCenter.current().delegate = self
    }

    func sendNotification(title: String, subtitle: String, body: String, timeInterval: TimeInterval) {
        let center = UNUserNotificationCenter.current()
        center.getPendingNotificationRequests(completionHandler: { pendingNotificationRequests in
            DispatchQueue.main.sync {
                let content = UNMutableNotificationContent()
                content.title = title
                content.subtitle = subtitle
                content.body = body
                let firingDate = Date().timeIntervalSince1970 + timeInterval
                content.userInfo = ["timeInterval": firingDate]
                let earlierNotificationsCount: Int = pendingNotificationRequests.filter { request in
                    let userInfo = request.content.userInfo
                    if let time = userInfo["timeInterval"] as? Double {
                        if time < firingDate {
                            return true
                        } else {
                            let newContent: UNMutableNotificationContent = request.content.mutableCopy() as! UNMutableNotificationContent
                            newContent.badge = (Int(truncating: request.content.badge ?? 0) + 1) as NSNumber
                            let newRequest: UNNotificationRequest =
                                UNNotificationRequest(identifier: request.identifier,
                                                      content: newContent,
                                                      trigger: request.trigger)
                            center.add(newRequest, withCompletionHandler: { (error) in
                                // Handle error
                            })
                            return false
                        }
                    }
                    return false
                    }.count
                content.badge =  NSNumber(integerLiteral: UIApplication.shared.applicationIconBadgeNumber + earlierNotificationsCount + 1)
                let trigger = UNTimeIntervalNotificationTrigger(timeInterval: timeInterval,
                                                                repeats: false)

                let requestIdentifier = UUID().uuidString  //You probably want to save these request identifiers if you want to remove the corresponding notifications later
                let request = UNNotificationRequest(identifier: requestIdentifier,
                                                    content: content, trigger: trigger)

                center.add(request, withCompletionHandler: { (error) in
                    // Handle error
                })
            }
        })
    }

    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        DispatchQueue.main.async {
            UIApplication.shared.applicationIconBadgeNumber += 1
        }
        completionHandler([.alert, .sound])
    }

    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        DispatchQueue.main.async {
            UIApplication.shared.applicationIconBadgeNumber -= 1
        }

        let center = UNUserNotificationCenter.current()
        center.getPendingNotificationRequests(completionHandler: {requests in
            let newRequests: [UNNotificationRequest] =
                requests
                    .filter{ rq in
                        return rq.content.userInfo["timeInterval"] is Double? 
                    }
                    .map { request in
                        let newContent: UNMutableNotificationContent = request.content.mutableCopy() as! UNMutableNotificationContent
                        newContent.badge = (Int(truncating: request.content.badge ?? 0) - 1) as NSNumber
                        let newRequest: UNNotificationRequest =
                            UNNotificationRequest(identifier: request.identifier,
                                              content: newContent,
                                              trigger: request.trigger)
                        return newRequest
            }
            newRequests.forEach { center.add($0, withCompletionHandler: { (error) in
                // Handle error
            })
            }
        })
        completionHandler()
    }
}
person ielyamani    schedule 16.09.2018
comment
После исправлений я удалил свой отрицательный голос и проголосовал за этот ответ. Но это такое сложное решение простой потребности. И это не применимо к уведомлениям, которые имеют триггер «местоположение». Ответ подходит только для уведомлений с триггером «время». Убедитесь, что вы видите это ДРУГОЕ обсуждение в чате. Основное обсуждение не происходило в чате, на который ссылался Самуэль... - person Honey; 20.09.2018
comment
@ Дорогая, спасибо, что помогла улучшить ответ ???????? - person ielyamani; 20.09.2018

Я предполагаю, что это все локальное уведомление.

AFAIK есть решение вашего вопроса!

Когда приходит уведомление, вы находитесь либо на переднем плане, либо на заднем плане.

  • передний план: вы получаете обратный вызов userNotificationCenter(_:willPresent:withCompletionHandler:), но я не думаю, что в этом случае вы захотите увеличить значок, верно? Потому что пользователь только что увидел это. Хотя я могу представить, где вам может понадобиться это сделать. Предположим, ваше приложение похоже на WhatsApp, и пользователь открыл приложение и отправляет сообщение своей матери. Затем приходит сообщение от его отца. На данный момент он не открывал сообщения между ним и отцом, но видит уведомление. В своем willPresent вы можете запросить getDeliveredNotifications и настроить количество значков.
  • background: для версии iOS10+ для локальных уведомлений вам не повезло! Потому что для вас НЕТ обратного звонка. Уведомление доставляется в ОС и все! Раньше был именованный application:didReceiveLocalNotification: , но это < strong>устарело. Подробнее об этом см. здесь
  • Когда пользователь нажимает (передний или задний план), вы получаете userNotificationCenter(_:didReceive:withCompletionHandler:) но это снова бесполезно, потому что пользователь уже подтвердил получение уведомления, и увеличение значка в этом случае не имеет смысла.

Короче говоря, насколько я знаю, вы ничего не можете сделать для локальных уведомлений.

Если это удаленное уведомление, то в application(_:didReceiveRemoteNotification:fetchCompletionHandler:) вы можете запросить доставленные уведомления и увеличить количество значков...

ИЗМЕНИТЬ:

Поскольку счетчик badgeCount прикреплен к прибывающему уведомлению, то, если вы можете обновить его badgeCount до прибытия, все в порядке. например в 12:00 вы всегда можете запросить список ожидающих уведомлений. Он предоставит вам все уведомления, поступающие после 12 часов дня, и при необходимости обновит для них значок значка, например. уменьшить их badgeCount, если некоторые доставленные уведомления прочитаны. Полное решение этой проблемы см. в ответе Carspen90. Суть его ответа

для любого нового уведомления, которое вы хотите отправить:

  1. получить pendingNotifications
  2. отфильтровать уведомления, у которых их FireDate наступает раньше, чем новое уведомление об отправке, и получить его count
  3. установите значок нового уведомления на badgeCount + filteredCount + 1 приложения
  4. if любое из ожидающих уведомлений имеет дату запуска больше, чем новое уведомление, которое мы только что добавили, мы увеличим badgeCount в ожидающем уведомлении на 1.
  5. очевидно, снова всякий раз, когда вы взаимодействуете с доставленными уведомлениями, вам нужно снова получить все pendingNotifications и уменьшить их badgeCount на 1

ВНИМАНИЕ:

Вы не можете сделать это для уведомлений, срабатывание которых основано на местоположении, потому что, очевидно, их не волнует время.

person Honey    schedule 16.09.2018
comment
это локальное уведомление. можете ли вы дать мне кодовое представление для вашего ответа, пожалуйста - person King; 16.09.2018
comment
Независимо от кода, вы поняли, что я сказал? В конечном счете, я говорю, что если вы находитесь в фоновом режиме, ОС не дает вам обратного вызова для доставки локальных уведомлений. Значит, вам почти не повезло. Если вы не хотите увеличить значок для примера уведомления отца/матери, который я привел. Но я тоже не рекомендую этого делать, потому что ваш счетчик значков не будет точным, когда приложение находится в фоновом режиме. И это может запутать пользователя. По крайней мере, насколько мне известно, для этого нет решения или взлома. - person Honey; 16.09.2018