Excluir uma notificação local específica

91

Estou desenvolvendo um aplicativo de alarme para iPhone baseado em notificações locais.

Ao excluir um alarme, a notificação local relacionada deve ser cancelada. Mas como posso determinar exatamente qual objeto da matriz de notificações locais deve ser cancelado?

Estou ciente do [[UIApplication sharedApplication] cancelLocalNotification:notification]método, mas como posso obter esta 'notificação' para cancelá-lo?

Iogue
fonte

Respostas:

217

Você pode salvar um valor exclusivo para a chave no userinfo de sua notificação local. Obtenha todas as notificações locais, percorra a matriz e exclua a notificação específica.

Codifique como segue,

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

RÁPIDO:

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

Notificação do usuário:

Se você usa UserNotification (iOS 10+), basta seguir estas etapas:

  1. Ao criar o conteúdo UserNotification, adicione um identificador exclusivo

  2. Remova notificações pendentes específicas usando removePendingNotificationRequests (withIdentifiers :)

  3. Remova notificações entregues específicas usando removeDeliveredNotifications (withIdentifiers :)

Para mais informações, UNUserNotificationCenter

KingofBliss
fonte
@ kingofBliss, pode me dizer para dar lá em "uidtodelete" .porque não está declarado no meu caso.
ishhhh
@ishhh é apenas um valor strig .. você deve declará-lo e inicializá-lo com um valor do uid a ser excluído
KingofBliss
@ kingofBliss, o uid está sempre mostrando null em NSLog.dont konw como se livrar disso. Por favor, me ajude
ishhhh
@ishhh você armazenou algum valor para uid no dicionário userinfo ao criar a notificação local? Eu acho que você perdeu isso.
KingofBliss,
@kingofBliss, o "uid" é um nome de sua própria variável, você pode usar qualquer nome significativo como "notificationID" e armazená-lo em a NSDictionarycom o valor do id da entidade relacionada ao UILocalNotification. Em seguida, defina a propriedade notification.userInfo para o dicionário com seus dados personalizados. Agora, quando você receber as notificações, poderá diferenciá-las com aquele id personalizado ou qualquer outra coisa de que precise.
IgniteCoders
23

Outra opção:

Em primeiro lugar, ao criar uma notificação local, você pode armazená-la nos padrões do usuário para uso futuro. O objeto de notificação local não pode ser armazenado diretamente nos padrões do usuário. Este objeto precisa ser convertido em objeto NSData primeiro e, em seguida, NSDatapode ser armazenado em User defaults. Abaixo está o código para isso:

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

Depois de armazenar e programar a notificação local, no futuro, pode surgir a necessidade de cancelar qualquer notificação criada anteriormente, para que você possa recuperá-la dos padrões do usuário.

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

Espero que isto ajude

iMOBDEV
fonte
Obrigado, eu implementei pela primeira vez, mas sua resposta também está correta. Vou levar isso em consideração. Você pode dizer qual é o mais eficiente? Obrigado pela ajuda :)
Yogi
1
@Yogi: Se você olhar a primeira resposta, você precisa executar o loop for todas as vezes se quiser cancelar a notificação local, mas na resposta acima, você não precisará executar nenhum loop for, você pode acessar diretamente a notificação local e cancelar isso notificação local e removê-la dos padrões do usuário. De acordo com minha resposta, é a maneira mais eficiente
iMOBDEV
@JigneshBrahmkhatri Seu método é eficaz. Mas ele falhará quando o usuário desinstalar o aplicativo e reinstalá-lo.
KingofBliss de
@KingofBliss, nesse caso temos que cancelar todas as notificações, certo? Então, acho que essa solução é mais rápida. :)
Sufian de
@Sufian Para cancelar todas as notificações, existe uma maneira muito mais rápida [[UIApplication sharedApplication] cancelAllLocalNotifications]; ;)
KingofBliss
8

Aqui está o que eu faço.

Ao criar sua notificação, faça o seguinte:

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

ao tentar excluí-lo, faça o seguinte:

 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

    }

}

Esta solução deve funcionar para notificações múltiplas, e você não está gerenciando nenhuma matriz, dicionário ou padrão do usuário. Você simplesmente usa os dados já salvos no banco de dados de notificação do sistema.

Espero que isso ajude futuros designers e desenvolvedores.

Boas equipes de programação! : D

abhi
fonte
Obrigado por compartilhar sua resposta, mas como essa lógica funciona se todas as suas notificações tiverem o mesmo corpo ou se o corpo for retirado do usuário. Nesse caso, o usuário pode dar o mesmo corpo a várias notificações.
Yogi de
@Yogi, como alertbody, você pode verificar, notification.firedate para obter a notificação necessária. obrigado a abhi por uma solução simples.upvote 1 para u
Azik Abdullah
1
@NAZIK: Obrigado pelo seu interesse na discussão. Mas ainda assim o usuário pode agendar duas notificações na mesma data de incêndio, pois é um aplicativo de alarme. Pelo menos pode ser um caso de teste para um testador e essa solução parece estar falhando nisso.
Yogi
@ Yogi, teste inteligente, por que não podemos verificar if ([localNotification.alertBody isEqualToString: savedTitle] || [localNotification.firedate == something]), já que as duas notificações com a mesma data devem conter alertBody diferente
Azik Abdullah
Não abuse do alertBodyou fireDatepara identificar uma notificação; use o userInfocampo para fazer isso, como a resposta por @KingOfBliss details ...
severin
8

Agendamento e remoção de notificação rapidamente:

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



}
Roman Barzyczak
fonte
1
Não abuse do alertBodyou fireDatepara identificar uma notificação; use o userInfocampo para fazer isso, como a resposta de @KingOfBliss details ...
severin
Sim alertBody não é uma boa opção para identificar uma notificação. Eu mudei para userInfo
Roman Barzyczak
6

A solução iMOBDEV funciona perfeitamente para remover uma notificação específica (por exemplo, após excluir o alarme), mas é especialmente útil quando você precisa remover seletivamente qualquer notificação que já foi disparada e ainda está na central de notificações.

Um cenário possível seria: a notificação para um alarme dispara, mas o usuário abre o aplicativo sem tocar na notificação e programa o alarme novamente. Se você quiser ter certeza de que apenas uma notificação pode estar na central de notificações para um determinado item / alarme, é uma boa abordagem. Ele também permite que você não tenha que limpar todas as notificações toda vez que o aplicativo for aberto, o que se adequa melhor ao aplicativo.

  • Ao criar uma notificação local, use NSKeyedArchiverpara armazená-la como Dataem UserDefaults. Você pode criar uma chave igual ao que está salvando no dicionário userInfo da notificação. Se estiver associado a um objeto Core Data, você pode usar sua propriedade objectID exclusiva.
  • Recupere-o com NSKeyedUnarchiver. Agora você pode excluí-lo usando o método cancelLocalNotification.
  • Atualize a chave de UserDefaultsacordo.

Aqui está uma versão Swift 3.1 dessa solução (para destinos abaixo do iOS 10):

Loja

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

Recuperar e excluir

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
fonte
Funcionou para mim. Todas as outras sugestões não, porque a matriz está vazia.
Maksim Kniazev
Alguma ideia para iOS 10?
Danpe
1
@Danpe: dê uma olhada na seção "Gerenciando notificações entregues" aqui: developer.apple.com/reference/usernotifications/…
Rygen
funcionou para mim com o swift 3 com mods menores, que o Xcode tratou.
beshio
@beshio: obrigado pelo aviso. Eu atualizei sua sintaxe.
Rygen
4

Versão Swift, se necessário:

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!")
            }
        }
    }
Sohil R. Memon
fonte
4

Solução Swift 4:

UNUserNotificationCenter.current().getPendingNotificationRequests { (requests) in
  for request in requests {
    if request.identifier == "identifier" {
      UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ["identifier"])
    }
  }
}   
Nupur Sharma
fonte
2

Você pode manter uma string com o identificador da categoria ao programar a notificação, como

        localNotification.category = NotificationHelper.categoryIdentifier

e pesquise e cancele quando necessário

let  app = UIApplication.sharedApplication()

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

        }
    }
pantos27
fonte
1

O objeto UILocalNotification que você passar cancelLocalNotification:corresponderá a qualquer objeto UILocalNotification existente com propriedades correspondentes.

Assim:

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

apresentará uma notificação local que pode ser cancelada posteriormente com:

UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = @"foo";
[[UIApplication sharedApplication] cancelLocalNotification:notification];
jibberd
fonte
1
Obrigado. Acho que você está criando uma nova notificação e, em seguida, cancelando-a. Não terá nenhum efeito na minha notificação agendada anteriormente e ainda será disparada.
Yogi de
Existe alguma propriedade que pode corresponder à propriedade, exceto alertBody?
Shamsiddin
1

Eu uso esta função no 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
  }

Inspirado na resposta de @KingofBliss

MBH
fonte
1

estilo 3 rápido:

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

}

para iOS 10, use:

    let center = UNUserNotificationCenter.current()
    center.removePendingNotificationRequests(withIdentifiers: [uidtodelete])
ingconti
fonte
0

Para lembretes repetidos (por exemplo, você deseja que seu alarme dispare no domingo, sábado e quarta-feira às 16h, então você deve fazer 3 alarmes e definir repeatInterval para NSWeekCalendarUnit).

Para fazer um lembrete único:

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

Para fazer lembretes repetidos:

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

Para Filtrar, você pode exibi-lo.

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

Para remover o lembrete, mesmo que seja apenas uma vez ou repetido:

- (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
fonte
0

Ampliei um pouco a resposta do KingofBliss, escrevi um pouco mais como o Swift2, removi alguns códigos desnecessários e adicionei alguns protetores de colisão.

Para começar, ao criar a notificação, você precisa ter certeza de definir o uid (ou qualquer propriedade personalizada) da notificação userInfo:

notification.userInfo = ["uid": uniqueid]

Então, ao excluí-lo, você pode fazer:

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)'")
    }
}
brandonscript
fonte
1
Por segurança, você poderia usar a instrução guard guard let app = UIApplication.sharedApplication () else {return false} para schedualedNotif em app.scheduledLocalNotifications {...} Então você não precisa forçar o desempacotamento no for-loop
troligtvis