Удалить конкретное локальное уведомление

92

Я разрабатываю приложение для будильника для iPhone на основе локальных уведомлений.

При удалении будильника соответствующее локальное уведомление должно быть отменено. Но как мне определить, какой именно объект из массива локальных уведомлений нужно отменить?

Мне известен [[UIApplication sharedApplication] cancelLocalNotification:notification]метод, но как я могу получить это «уведомление», чтобы отменить его?

Йог
источник

Ответы:

217

Вы можете сохранить уникальное значение для ключа в информации пользователя вашего локального уведомления. Получить все локальные уведомления, пройти через массив и удалить конкретное уведомление.

Код следующим образом:

OBJ-C:

UIApplication *app = [UIApplication sharedApplication];
NSArray *eventArray = [app scheduledLocalNotifications];
for (int i=0; i<[eventArray count]; i++)
{
    UILocalNotification* oneEvent = [eventArray objectAtIndex:i];
    NSDictionary *userInfoCurrent = oneEvent.userInfo;
    NSString *uid=[NSString stringWithFormat:@"%@",[userInfoCurrent valueForKey:@"uid"]];
    if ([uid isEqualToString:uidtodelete])
    {
        //Cancelling local notification
        [app cancelLocalNotification:oneEvent];
        break;
    }
}

SWIFT:

var app:UIApplication = UIApplication.sharedApplication()
for oneEvent in app.scheduledLocalNotifications {
    var notification = oneEvent as UILocalNotification
    let userInfoCurrent = notification.userInfo! as [String:AnyObject]
    let uid = userInfoCurrent["uid"]! as String
    if uid == uidtodelete {
        //Cancelling local notification
        app.cancelLocalNotification(notification)
        break;
    }
}

Уведомление пользователя:

Если вы используете UserNotification (iOS 10+), просто выполните следующие действия:

  1. При создании содержимого UserNotification добавьте уникальный идентификатор

  2. Удалите конкретное ожидающее уведомление с помощью removePendingNotificationRequests (withIdentifiers :)

  3. Удалите конкретное доставленное уведомление с помощью removeDeliveredNotifications (withIdentifiers :)

Для получения дополнительной информации UNUserNotificationCenter

KingofBliss
источник
@ kingofBliss, не могли бы вы сказать мне отдать там на "uidtodelete", потому что в моем случае это не заявлено.
ishhhh
@ishhh это просто значение strig .. вы должны объявить его и инициализировать со значением удаляемого uid
KingofBliss 02
@ kingofBliss, uid всегда показывает null в NSLog. не знаю, как от этого избавиться. Пожалуйста, помогите мне
ishhhh
@ishhh вы сохранили какое-либо значение для uid в словаре userinfo при создании локального уведомления? Я думаю, вы это упустили.
KingofBliss
@kingofBliss, «uid» - это имя вашей собственной переменной, вы можете использовать любое значимое имя, например «notificationID», и сохранить его в a NSDictionaryсо значением идентификатора объекта, связанного с UILocalNotification. Затем установите для свойства notification.userInfo словарь с вашими пользовательскими данными. Теперь, когда вы получаете уведомления, вы можете различать их с помощью этого пользовательского идентификатора или чего-то еще, что вам может понадобиться.
IgniteCoders
23

Другой вариант:

Прежде всего, когда вы создаете локальное уведомление, вы можете сохранить его в пользовательских значениях по умолчанию для будущего использования. Локальный объект уведомления не может быть сохранен непосредственно в пользовательских настройках по умолчанию. Этот объект необходимо сначала преобразовать в объект NSData, а затем NSDataего можно сохранить в User defaults. Ниже приведен код для этого:

NSData *data = [NSKeyedArchiver archivedDataWithRootObject:localNotif];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:[NSString  stringWithFormat:@"%d",indexPath.row]];

После того, как вы сохранили и запланировали локальное уведомление, в будущем может возникнуть требование отменить любое уведомление, которое вы создали ранее, чтобы вы могли получить его из пользовательских настроек по умолчанию.

NSData *data= [[NSUserDefaults standardUserDefaults] objectForKey:[NSString   stringWithFormat:@"%d",UniqueKey]];

UILocalNotification *localNotif = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(@"Remove localnotification  are %@", localNotif);
[[UIApplication sharedApplication] cancelLocalNotification:localNotif];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:[NSString stringWithFormat:@"%d",UniqueKey]];

Надеюсь это поможет

iMOBDEV
источник
Спасибо, я реализовал это первым способом, но ваш ответ тоже правильный. Я учту это. Не могли бы вы сказать, какой из них более эффективен? Спасибо за помощь :)
Йоги
1
@Yogi: если вы посмотрите на первый ответ, вам нужно запускать цикл for каждый раз, если вы хотите отменить локальное уведомление, но в приведенном выше ответе вам не нужно запускать цикл for, вы можете напрямую получить доступ к локальному уведомлению и отменить это локальное уведомление и удалите его из пользовательских настроек по умолчанию. Согласно моему ответу, это более эффективный способ
iMOBDEV
@JigneshBrahmkhatri Ваш метод эффективен. Но это не удастся, когда пользователь удалит приложение и переустановит его.
KingofBliss
@KingofBliss, в этом случае мы должны отменить все уведомления, верно? Думаю, это решение быстрее. :)
Sufian
@Sufian Для отмены всех уведомлений существует гораздо более быстрый способ [[UIApplication sharedApplication] cancelAllLocalNotifications]; ;)
KingofBliss
8

Вот что я делаю.

При создании уведомления сделайте следующее:

  // Create the notification

UILocalNotification *notification = [[UILocalNotification alloc]  init] ;



notification.fireDate = alertDate;
notification.timeZone = [NSTimeZone localTimeZone] ;
notification.alertAction = NSLocalizedString(@"Start", @"Start");
notification.alertBody = **notificationTitle**;
notification.repeatInterval= NSMinuteCalendarUnit;

notification.soundName=UILocalNotificationDefaultSoundName;
notification.applicationIconBadgeNumber = 1;

[[UIApplication sharedApplication] scheduleLocalNotification:notification] ;

при попытке удалить это сделать так:

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

for (UILocalNotification *localNotification in arrayOfLocalNotifications) {

    if ([localNotification.alertBody isEqualToString:savedTitle]) {
        NSLog(@"the notification this is canceld is %@", localNotification.alertBody);

        [[UIApplication sharedApplication] cancelLocalNotification:localNotification] ; // delete the notification from the system

    }

}

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

Надеюсь, это поможет будущим дизайнерам и разработчикам.

Удачного кодирования, ребята! : D

абхи
источник
Спасибо, что поделились своим ответом, но как эта логика работает, если все ваши уведомления имеют одно и то же тело или если тело должно быть взято у пользователя. В этом случае пользователь может указать одно и то же тело для нескольких уведомлений.
Йоги
@Yogi, как и alertbody, вы можете проверить, notification.firedate, чтобы получить необходимое уведомление. спасибо abhi за простое решение. проголосовать 1 за u
Назик
1
@NAZIK: Спасибо за интерес к обсуждению. Но все же пользователь может запланировать два уведомления на одну и ту же дату возгорания, поскольку это приложение для сигнализации. По крайней мере, это может быть тестовый пример для тестировщика, и это решение, похоже, не работает.
Йоги
@ Йог, мудрое тестирование, почему мы не можем проверить, если ([localNotification.alertBody isEqualToString: savedTitle] || [localNotification.firedate == something]), поскольку два уведомления с одинаковой датой должны содержать разные alertBody
Назик
Не злоупотребляйте alertBodyили fireDateдля идентификации уведомления; используйте userInfoдля этого поле, как ответ @KingOfBliss details ...
severin
8

Планирование и быстрое удаление уведомлений:

    static func scheduleNotification(notificationTitle:String, objectId:String) {

    var localNotification = UILocalNotification()
    localNotification.fireDate = NSDate(timeIntervalSinceNow: 24*60*60)
    localNotification.alertBody = notificationTitle
    localNotification.timeZone = NSTimeZone.defaultTimeZone()
    localNotification.applicationIconBadgeNumber = 1
    //play a sound
    localNotification.soundName = UILocalNotificationDefaultSoundName;
    localNotification.alertAction = "View"
    var infoDict :  Dictionary<String,String!> = ["objectId" : objectId]
    localNotification.userInfo = infoDict;

    UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
}
    static func removeNotification(objectId:String) {
    var app:UIApplication = UIApplication.sharedApplication()

    for event in app.scheduledLocalNotifications {
        var notification = event as! UILocalNotification
        var userInfo:Dictionary<String,String!> = notification.userInfo as! Dictionary<String,String!>
        var infoDict :  Dictionary = notification.userInfo as! Dictionary<String,String!>
        var notifcationObjectId : String = infoDict["objectId"]!

        if notifcationObjectId == objectId {
            app.cancelLocalNotification(notification)
        }
    }



}
Роман Барзичак
источник
1
Не злоупотребляйте alertBodyили fireDateдля идентификации уведомления; используйте userInfoдля этого поле, как ответ @KingOfBliss details ...
severin
Да, alertBody - не лучший вариант для идентификации уведомлений. Я поменял его на userInfo
Роман Барзичак
6

iMOBDEV в решении отлично работает , чтобы удалить уведомления конкретных (например , после удаления сигнала тревоги) , но это особенно полезно , когда вам нужно выборочно удалить любое уведомление , что уже уволены и по - прежнему в центре уведомлений.

Возможный сценарий: срабатывает уведомление о тревоге, но пользователь открывает приложение, не нажимая на это уведомление, и снова планирует эту тревогу. Если вы хотите убедиться, что в центре уведомлений для данного элемента / сигнала тревоги может быть только одно уведомление, это хороший подход. Это также позволяет вам не очищать все уведомления каждый раз при открытии приложения, это должно лучше соответствовать приложению.

  • После создания локального уведомления используйте, NSKeyedArchiverчтобы сохранить его как Dataв UserDefaults. Вы можете создать ключ, равный тому, который вы сохраняете в словаре userInfo уведомления. Если он связан с объектом Core Data, вы можете использовать его уникальное свойство objectID.
  • Получите его с помощью NSKeyedUnarchiver. Теперь вы можете удалить его с помощью метода cancelLocalNotification.
  • Обновите ключ UserDefaultsсоответственно.

Вот версия этого решения для Swift 3.1 (для целей ниже iOS 10):

хранить

// localNotification is the UILocalNotification you've just set up
UIApplication.shared.scheduleLocalNotification(localNotification)
let notificationData = NSKeyedArchiver.archivedData(withRootObject: localNotification)
UserDefaults.standard.set(notificationData, forKey: "someKeyChosenByYou")

Получить и удалить

let userDefaults = UserDefaults.standard
if let existingNotificationData = userDefaults.object(forKey: "someKeyChosenByYou") as? Data,
    let existingNotification = NSKeyedUnarchiver.unarchiveObject(with: existingNotificationData) as? UILocalNotification {

    // Cancel notification if scheduled, delete it from notification center if already delivered    
    UIApplication.shared.cancelLocalNotification(existingNotification)

    // Clean up
    userDefaults.removeObject(forKey: "someKeyChosenByYou")
}
Rygen
источник
Работал у меня. Всех других предложений нет, потому что массив пуст.
Максим Князев
Есть идеи для iOS 10?
Danpe
1
@Danpe: взгляните на раздел «Управление доставленными уведомлениями» здесь: developer.apple.com/reference/usernotifications/…
Rygen
работал у меня с Swift 3 с небольшими модами, которые обрабатывались Xcode.
beshio
@beshio: спасибо за внимание. Я обновил его синтаксис.
Rygen
5

Решение Swift 4:

UNUserNotificationCenter.current().getPendingNotificationRequests { (requests) in
  for request in requests {
    if request.identifier == "identifier" {
      UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ["identifier"])
    }
  }
}   
Нупур Шарма
источник
4

Версия Swift, если нужно:

func cancelLocalNotification(UNIQUE_ID: String){

        var notifyCancel = UILocalNotification()
        var notifyArray = UIApplication.sharedApplication().scheduledLocalNotifications

        for notifyCancel in notifyArray as! [UILocalNotification]{

            let info: [String: String] = notifyCancel.userInfo as! [String: String]

            if info[uniqueId] == uniqueId{

                UIApplication.sharedApplication().cancelLocalNotification(notifyCancel)
            }else{

                println("No Local Notification Found!")
            }
        }
    }
Сохил Р. Мемон
источник
2

Вы можете сохранить строку с идентификатором категории при планировании уведомления, например

        localNotification.category = NotificationHelper.categoryIdentifier

и найдите его и отмените при необходимости вот так

let  app = UIApplication.sharedApplication()

    for notification in app.scheduledLocalNotifications! {
        if let cat = notification.category{
            if cat==NotificationHelper.categoryIdentifier {
                app.cancelLocalNotification(notification)
                break
            }

        }
    }
pantos27
источник
2

быстрый 3-стиль:

final private func cancelLocalNotificationsIfIOS9(){


//UIApplication.shared.cancelAllLocalNotifications()
let app = UIApplication.shared
guard let notifs = app.scheduledLocalNotifications else{
    return
}

for oneEvent in notifs {
    let notification = oneEvent as UILocalNotification
    if let userInfoCurrent = notification.userInfo as? [String:AnyObject], let uid = userInfoCurrent["uid"] as? String{
        if uid == uidtodelete {
            //Cancelling local notification
            app.cancelLocalNotification(notification)
            break;
        }
    }
}

}

для iOS 10 используйте:

    let center = UNUserNotificationCenter.current()
    center.removePendingNotificationRequests(withIdentifiers: [uidtodelete])
Ingconti
источник
1

Объект UILocalNotification, которому вы передаете, cancelLocalNotification:будет соответствовать любому существующему объекту UILocalNotification с соответствующими свойствами.

Так:

UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = @"foo";
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];

представит локальное уведомление, которое позже можно отменить с помощью:

UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = @"foo";
[[UIApplication sharedApplication] cancelLocalNotification:notification];
джибберд
источник
1
Спасибо. Я думаю, вы создаете новое уведомление, а затем отменяете его. Это не повлияет на мое ранее запланированное уведомление, и оно все равно будет запущено.
Йоги
Есть ли какое-либо свойство, которое может соответствовать свойству, кроме alertBody?
Shamsiddin
1

Я использую эту функцию в Swift 2.0:

  static func DeleteNotificationByUUID(uidToDelete: String) -> Bool {
    let app:UIApplication = UIApplication.sharedApplication()
    // loop on all the current schedualed notifications
    for schedualedNotif in app.scheduledLocalNotifications! {
      let notification = schedualedNotif as UILocalNotification
      let urrentUi = notification.userInfo! as! [String:AnyObject]
      let currentUid = urrentUi["uid"]! as! String
      if currentUid == uidToDelete {
        app.cancelLocalNotification(notification)
        return true
      }
    }
    return false
  }

На основе ответа @ KingofBliss

MBH
источник
0

Для повторяющихся напоминаний (например, вы хотите, чтобы ваш будильник срабатывал в воскресенье, субботу и среду в 16:00, затем вы должны сделать 3 будильника и установить для параметра repeatInterval значение NSWeekCalendarUnit).

Для создания однократного напоминания:

UILocalNotification *aNotification = [[UILocalNotification alloc] init];
                aNotification.timeZone = [NSTimeZone defaultTimeZone];
                aNotification.alertBody = _reminderTitle.text;
                aNotification.alertAction = @"Show me!";
                aNotification.soundName = UILocalNotificationDefaultSoundName;
                aNotification.applicationIconBadgeNumber += 1;

                NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
                NSDateComponents *componentsForFireDate = [calendar components:(NSYearCalendarUnit | NSWeekCalendarUnit|  NSHourCalendarUnit | NSMinuteCalendarUnit| NSSecondCalendarUnit | NSWeekdayCalendarUnit) fromDate: _reminderDate];

                [componentsForFireDate setHour: [componentsForFireDate hour]] ; //for fixing 8PM hour
                [componentsForFireDate setMinute:[componentsForFireDate minute]];

                [componentsForFireDate setSecond:0] ;
                NSDate *fireDateOfNotification = [calendar dateFromComponents: componentsForFireDate];
                aNotification.fireDate = fireDateOfNotification;
                NSDictionary *infoDict = [NSDictionary dictionaryWithObject:_reminderTitle.text forKey:kRemindMeNotificationDataKey];
                aNotification.userInfo = infoDict;

                [[UIApplication sharedApplication] scheduleLocalNotification:aNotification];

Для повторного напоминания:

for (int i = 0 ; i <reminderDaysArr.count; i++)
                {

                    UILocalNotification *aNotification = [[UILocalNotification alloc] init];
                    aNotification.timeZone = [NSTimeZone defaultTimeZone];
                    aNotification.alertBody = _reminderTitle.text;
                    aNotification.alertAction = @"Show me!";
                    aNotification.soundName = UILocalNotificationDefaultSoundName;
                    aNotification.applicationIconBadgeNumber += 1;

                    NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
                    NSDateComponents *componentsForFireDate = [calendar components:(NSYearCalendarUnit | NSWeekCalendarUnit|  NSHourCalendarUnit | NSMinuteCalendarUnit| NSSecondCalendarUnit | NSWeekdayCalendarUnit) fromDate: _reminderDate];


                    [componentsForFireDate setWeekday: [[reminderDaysArr objectAtIndex:i]integerValue]];

                    [componentsForFireDate setHour: [componentsForFireDate hour]] ; // Setup Your Own Time.
                    [componentsForFireDate setMinute:[componentsForFireDate minute]];

                    [componentsForFireDate setSecond:0] ;
                    NSDate *fireDateOfNotification = [calendar dateFromComponents: componentsForFireDate];
                    aNotification.fireDate = fireDateOfNotification;
                    aNotification.repeatInterval = NSWeekCalendarUnit;
                    NSDictionary *infoDict = [NSDictionary dictionaryWithObject:_reminderTitle.text forKey:kRemindMeNotificationDataKey];
                    aNotification.userInfo = infoDict;

                    [[UIApplication sharedApplication] scheduleLocalNotification:aNotification];
                }
            }

Для фильтрации вы массив для его отображения.

-(void)filterNotficationsArray:(NSMutableArray*) notificationArray{

    _dataArray = [[NSMutableArray alloc]initWithArray:[[UIApplication sharedApplication] scheduledLocalNotifications]];
    NSMutableArray *uniqueArray = [NSMutableArray array];
    NSMutableSet *names = [NSMutableSet set];

    for (int i = 0 ; i<_dataArray.count; i++) {
        UILocalNotification *localNotification = [_dataArray objectAtIndex:i];
        NSString * infoDict = [localNotification.userInfo objectForKey:@"kRemindMeNotificationDataKey"];

        if (![names containsObject:infoDict]) {
            [uniqueArray addObject:localNotification];
            [names addObject:infoDict];
        }
    }
    _dataArray = uniqueArray;
}

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

- (void) removereminder:(UILocalNotification*)notification
{
    _dataArray = [[NSMutableArray alloc]initWithArray:[[UIApplication sharedApplication]scheduledLocalNotifications]];

    NSString * idToDelete = [notification.userInfo objectForKey:@"kRemindMeNotificationDataKey"];
    for (int i = 0 ; i<_dataArray.count; i++)
    {
        UILocalNotification *currentLocalNotification = [_dataArray objectAtIndex:i];
        NSString * notificationId = [currentLocalNotification.userInfo objectForKey:@"kRemindMeNotificationDataKey"];

        if ([notificationId isEqualToString:idToDelete])
            [[UIApplication sharedApplication]cancelLocalNotification:currentLocalNotification];
    }

    _dataArray = [[NSMutableArray alloc]initWithArray:[[UIApplication sharedApplication]scheduledLocalNotifications]];
    [self filterNotficationsArray:_dataArray];
    [_remindersTV reloadData];

}
Abo3atef
источник
0

Я немного расширил ответ KingofBliss, написал это немного более похожим на Swift2, удалил ненужный код и добавил некоторые аварийные предохранители.

Для начала, при создании уведомления вам необходимо убедиться, что вы установили uid (или любое другое настраиваемое свойство) уведомления userInfo:

notification.userInfo = ["uid": uniqueid]

Затем, удаляя его, вы можете:

guard
    let app: UIApplication = UIApplication.sharedApplication(),
    let notifications = app.scheduledLocalNotifications else { return }
for notification in notifications {
    if
        let userInfo = notification.userInfo,
        let uid: String = userInfo["uid"] as? String where uid == uidtodelete {
            app.cancelLocalNotification(notification)
            print("Deleted local notification for '\(uidtodelete)'")
    }
}
брендонскрипт
источник
1
В целях безопасности вы можете использовать guard-statement guard let app = UIApplication.sharedApplication () else {return false} для schedualedNotif в app.scheduledLocalNotifications {...} Тогда вам не нужно принудительно развертывать его в цикле for
troligtvis