美文网首页iOS开发(OC)iOS开发转载
iOS本地推送通知的简单封装(定时提醒、重复提醒)

iOS本地推送通知的简单封装(定时提醒、重复提醒)

作者: MrZombie | 来源:发表于2019-01-04 15:42 被阅读0次

    实现快捷创建简单的定时重复提醒推送功能。

    主要实现原理

    iOS10及以上

    1.获取通知权限

    iOS10及以上要先请求通知权限

    UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];
        //请求获取通知权限(角标,声音,弹框)
        [center requestAuthorizationWithOptions:(UNAuthorizationOptionBadge |
                                                 UNAuthorizationOptionSound |
                                                 UNAuthorizationOptionAlert)
                              completionHandler:^(BOOL granted, NSError * _Nullable error) {
            if (granted) {
                //获取用户是否同意开启通知
                NSLog(@"开启通知成功!");
    
            }
        }];
        
    

    2.创建通知

    UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
        content.title = attribute[ZBNotificationAlertTitle];
    //    content.subtitle = @"本地通知副标题";
        content.body = @"body";
        //角标数量
        content.badge = @1;
        content.userInfo = userInfo;
    
        //设置通知声音
        UNNotificationSound *sound = [UNNotificationSound defaultSound];
        content.sound = sound;
        
    

    创建 UNNotificationRequest :

    //设置时间容器:传人date中所有时间元素放入时间容器
    NSDateComponents * components = [[NSCalendar currentCalendar]
                                         components:NSCalendarUnitYear |
                                         NSCalendarUnitMonth |
                                         NSCalendarUnitWeekday |
                                         NSCalendarUnitDay |
                                         NSCalendarUnitHour |
                                         NSCalendarUnitMinute |
                                         NSCalendarUnitSecond
                                         fromDate:date];
    //设置UNCalendarNotificationTrigger
    //repeats: 设置是否重复
    UNCalendarNotificationTrigger * trigger = [UNCalendarNotificationTrigger 
                                                triggerWithDateMatchingComponents:components 
                                                repeats:repeat];
    //设置UNNotificationRequest
    //identifer:设置通知标识符或者说通知名字
    UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifer content:content trigger:trigger];
    
    

    3.添加通知

    //把通知加到UNUserNotificationCenter, 到指定触发点会被触发
    [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
        if (error) {
            NSLog(@"通知添加失败:%@",error);
        } else {
            NSLog(@"通知添加成功");
        }
    }];
    
    

    4.重复提醒

    如果repeats为YES时为重复提醒
    约定在特定时间提醒时我们用时间容器来实现

    NSDateComponents * components = [[NSCalendar currentCalendar]
                                         components:NSCalendarUnitYear |
                                         NSCalendarUnitMonth |
                                         NSCalendarUnitWeekday |
                                         NSCalendarUnitDay |
                                         NSCalendarUnitHour |
                                         NSCalendarUnitMinute |
                                         NSCalendarUnitSecond
                                         fromDate:date];
                                         
    

    比如设置一个每天都提醒的推送,传入的时间设置为:

    NSDateFormatter * formatter = [[NSDateFormatter alloc]init];
    [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSDate * date = [formatter dateFromString:@"2019-01-01 08:00:00"];
    
    

    每天8点 进行推送提醒的 时间容器 为:

    NSDateComponents * components = [[NSCalendar currentCalendar]
                                         components:NSCalendarUnitHour |
                                                    NSCalendarUnitMinute |
                                                    NSCalendarUnitSecond
                                         fromDate:date];
                                         
    

    每周二8点 (19年1月1号为周二) 进行推送提醒的 时间容器 为:

    NSDateComponents * components = [[NSCalendar currentCalendar]
                                         components:NSCalendarUnitWeekday |
                                         NSCalendarUnitHour |
                                         NSCalendarUnitMinute |
                                         NSCalendarUnitSecond
                                         fromDate:date];
                                         
    

    每月1号8点 进行推送提醒的 时间容器 为:

    NSDateComponents * components = [[NSCalendar currentCalendar]
                                         components:NSCalendarUnitDay |
                                         NSCalendarUnitHour |
                                         NSCalendarUnitMinute |
                                         NSCalendarUnitSecond
                                         fromDate:date];
                                         
    

    每年1月1号8点 进行推送提醒的 时间容器 为:

    NSDateComponents * components = [[NSCalendar currentCalendar]
                                         components:NSCalendarUnitMonth |
                                         NSCalendarUnitDay |
                                         NSCalendarUnitHour |
                                         NSCalendarUnitMinute |
                                         NSCalendarUnitSecond
                                         fromDate:date];
                                         
    

    以此类推..
    如果有需求是要 周一到周五的每天8点 提醒时,我的做法是用for循环创建5个通知:

    //因为周日是一星期第一天,1代表周日,所以周一从2开始
    for (NSInteger i = 2; i <= 6; i++) {
    //这里时间容器创建和以上每周提醒的一样,省略。
            components.weekday = i;
            
    //然后用这个components去添加通知就可以实现重复通知了
    }
    
    

    5.取消通知

    //找到要取消的通知名字
    NSMutableArray * names = [[NSMutableArray alloc]initWithObjects:notificationName, nil];
    //批量取消这些通知
    [[UNUserNotificationCenter currentNotificationCenter] removePendingNotificationRequestsWithIdentifiers:names];
    
    

    iOS10以下

    1.创建通知

    UILocalNotification *localNotification = [[UILocalNotification alloc]init];
        
        // 设置触发时间
        localNotification.fireDate = date;
        // 设置时区  以当前手机运行的时区为准
        localNotification.timeZone = [NSTimeZone localTimeZone];
        // 设置推送 显示的内容
        localNotification.alertTitle = @"title";
        localNotification.alertBody = @"body";
        // 设置 角标
        localNotification.applicationIconBadgeNumber = 1;
        // 不设置此属性,则默认不重复
        localNotification.repeatInterval =  repeatInterval;
        
        // 设置推送的声音
        localNotification.soundName =  UILocalNotificationDefaultSoundName;
        
        
        localNotification.userInfo = userInfo;
        //添加到通知
        [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
        
    

    2.重复提醒

    重复提醒通过设置 repeatInterval 就可以

    value effect
    NSCalendarUnitDay 每天
    NSCalendarUnitWeekday 每周
    NSCalendarUnitMonth 每月
    NSCalendarUnitYear 每年
    ... ...

    等等...
    同样如果有需求是周一到周五某时提醒也用循环创建五个通知

    for (NSInteger i = 2; i <= 6; i++) {
    //同样用NSDateComponents设置weekday来实现星期几的提醒
    //先用传入的date创建时间容器,然后修改weekday后再转为NSDate
        NSDateComponents * components = [[NSCalendar currentCalendar] components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond fromDate:date];
        components.weekday = i;
        NSDate * newDate = [[NSCalendar currentCalendar] dateFromComponents:components];
    //吧newDate再赋值给fireDate
    }
    
    

    3.取消通知

    //获取所有通知
    NSArray * localNotifications = [[UIApplication sharedApplication] scheduledLocalNotifications];
        for (UILocalNotification * notification in localNotifications) {
            //找到和要删除的通知同名的通知删除
            if ([notification.userInfo[@"notificationName"] hasPrefix:notificationName]) {
                //删除通知
                [[UIApplication sharedApplication] cancelLocalNotification:notification];
    
            }
        }
        
    

    注册通知

    //注册通知
    [[UIApplication sharedApplication] registerForRemoteNotifications];
        
    

    快捷使用

    封装了一个可以直接使用的类:ZBLocalNotification

    //提醒重复类型
    typedef NS_ENUM(NSInteger, ZBLocalNotificationRepeat) {
        ZBLocalNotificationRepeatNone,          //不重复
        ZBLocalNotificationRepeatEveryDay,      //每天
        ZBLocalNotificationRepeatEveryWeek,     //每周
        ZBLocalNotificationRepeatEveryMonth,    //每月
        ZBLocalNotificationRepeatEveryYear,     //每年
        ZBLocalNotificationRepeatEveryWorkDay   //每周一到周五(工作日)
    };
    
    //标识通知属性的key
    typedef NSString * ZBLocalNotificationKey;
    //标识通知声音文件名字的key
    typedef NSString * ZBLocalNotificationSoundName;
    
    extern ZBLocalNotificationKey const ZBNotificationFireDate;     //标识提醒时间
    extern ZBLocalNotificationKey const ZBNotificationAlertTitle;   //标识标题
    extern ZBLocalNotificationKey const ZBNotificationAlertBody;    //标识提醒内容
    extern ZBLocalNotificationKey const ZBNotificationAlertAction;  //标识按钮
    extern ZBLocalNotificationKey const ZBNotificationSoundName;    //标识声音
    extern ZBLocalNotificationKey const ZBNotificationUserInfoName; //标识通知名字
    extern ZBLocalNotificationKey const ZBNotificationPriority;     //标识通知优先级
    extern ZBLocalNotificationKey const ZBNotificationRepeat;       //标识通知重复
    extern ZBLocalNotificationSoundName const ZBNotificationSoundAlarm; //标识声音为提醒
    extern ZBLocalNotificationSoundName const ZBNotificationSoundOther; //标识声音为其他
    
    @interface ZBLocalNotification : NSObject
    
    /**
     创建本地通知
    
     @param attribute 通知的属性
     */
    + (void)createLocalNotificationWithAttribute:(NSDictionary *)attribute;
    
    /**
     取消通知
    
     @param notificationName 通知名字
     */
    + (void)cancelLocalNotificationWithName:(NSString *)notificationName;
    
    #ifdef NSFoundationVersionNumber_iOS_9_x_Max
    
    /**
     注册通知
     */
    + (void)requestUNUserNotificationAuthorization;
    
    #endif
    
    @end
    

    创建时间为date的每天重复提醒例子:
    导入#import "ZBLocalNotification.h"

    [ZBLocalNotification createLocalNotificationWithAttribute:
                                    @{ZBNotificationUserInfoName:@"notificationName",
                                      ZBNotificationSoundName:ZBNotificationSoundAlarm,
                                      ZBNotificationAlertBody:@"提醒内容",
                                      ZBNotificationAlertTitle:@"提醒标题",
                                      ZBNotificationFireDate:date,
                                      ZBNotificationPriority:@(0),
                                      ZBNotificationRepeat:@(ZBLocalNotificationRepeatEveryDay)
                                      }];
                                      
    

    取消名字为 “notificationName” 的通知:

    [ZBLocalNotification cancelLocalNotificationWithName:@"notificationName"];
    
    

    通知优先级问题

    如果设置了多个推送通知,并且时间都在同一个时刻时,就是同时收到多个推送通知时,想要只显示优先级最高的一个,我是在AppDelegate里这样处理的(希望有更好的方法能不吝赐教 :) ):
    先创建一个userInfo容器,保存同一时间收到的通知
    self.userInfos = [[NSMutableArray alloc]init];

    //处理接收到的通知信息
    - (void)filteredUserInfo {
        if (self.userInfos.count == 0) {
            return;
        }
        //选出你希望显示的通知信息,以下方法是显示优先级高的,你可以判断不同的条件
        //排序所有收到的通知信息,对比优先级,把优先级最高的放首位
        [self.userInfos sortUsingComparator:^NSComparisonResult(NSDictionary * obj1, NSDictionary * obj2) {
            if ([obj1[ZBNotificationPriority] integerValue] < [obj2[ZBNotificationPriority] integerValue]) {
                return NSOrderedDescending;
            }
            return NSOrderedSame;
        }];
        
        //自定义展示推送内容
        [self showAlarmAlertWithUserInfo:self.userInfos.firstObject];
        //重置userInfo容器
        [self.userInfos removeAllObjects];
    }
    //添加通知到userInfo容器
    - (void)waitMultipleUserInfo:(NSDictionary *)userInfo {
        [self.userInfos addObject:userInfo];
        //创建信号量,设置为0
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        dispatch_async(quene, ^{
            
            if (self.userInfos.count == 1) {
                //信号量为0时,那么这个函数就阻塞当前线程等待timeout,时间到后继续执行
                //0.3秒内第一次进入则等待0.3秒,0.3秒后对本时间段内提醒提取优先级最高的一个
                //就是保存在极短时间内(我这里设置为0.3s)收到的所有通知,然后进行处理
                dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 0.3 * NSEC_PER_SEC));
                dispatch_async(dispatch_get_main_queue(), ^{
                    [self filteredUserInfo];
                });
            }
        });
        
    }
    
    #pragma mark - localNotification
    // iOS10以下 在前台收到推送回调
    - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{
        [self waitMultipleUserInfo:notification.userInfo];
    }
    
    #ifdef NSFoundationVersionNumber_iOS_9_x_Max
    
    #pragma mark - UNUserNotificationCenterDelegate
    // iOS10 在前台收到推送回调
    - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(nonnull UNNotification *)notification withCompletionHandler:(nonnull void (^)(UNNotificationPresentationOptions))completionHandler{
        UNNotificationRequest *request = notification.request; // 收到推送的请求
        UNNotificationContent *content = request.content; //收到推送消息的全部内容
        
        if ([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
            NSLog(@"iOS10 收到远程通知");
        }else{
            NSLog(@"ios10 收到本地通知userInfo:%@",content.userInfo);
            [self waitMultipleUserInfo:content.userInfo];
        }
        completionHandler(UNNotificationPresentationOptionBadge |
                          UNNotificationPresentationOptionSound
                           );
        
    }
    
    #endif
    
    

    代码下载

    ZBLocalNotification类下载地址:
    iOS本地推送封装(定时推送、重复提醒)
    ZBLocalNotification类下载地址:
    iOS本地推送封装(定时推送、重复提醒)

    疑问

    比如需求是可以自定义重复的时间:

    • 每隔两周的周二早上8点提醒
    • 每隔三个月的7号早上8点提醒

    这种时候的怎么实现,NSDateComponents需要怎么设置?

    谢谢!

    欢迎讨论,未完待续


    相关文章

      网友评论

        本文标题:iOS本地推送通知的简单封装(定时提醒、重复提醒)

        本文链接:https://www.haomeiwen.com/subject/etqkrqtx.html