UILocalNotification本地通知

什么是本地通知
  • 顾名思义,不需要联网就能发送的通知,无需服务器.
本地通知使用场景
  • 常用来提醒用户完成一些定时任务,比如
  • 清理垃圾,买衣服,看电影,睡觉,运动等

1. 如何发送本地推送通知


推送通知也属于UI的一部分,所以推送通知对象是以UI开头

方法送通知的代码方法控制器的-touchesBegan: withEvent: 中测试,比较合适,放到viewDidLoad方法,用户的注册请求还没有完成方法就调用了

  • 创建本地通知
//  创建本地通知对象
    UILocalNotification *ln = [[UILocalNotification alloc] init];
  • 设置本地通知属性(推荐一个一个属性测试运行)
//  1.设置通知的内容(如果此属性不设置是不会发送通知的)
    ln.alertBody = @"小明,你妈叫你回家吃饭了!";
//  2.设置通知触发的开始时间
    ln.fireDate = [NSDate dateWithTimeIntervalSinceNow:3];
//  3.设置重复通知的时间,间隔
    ln.repeatInterval = NSCalendarUnitSecond;
//  4.设置重复执行使用日历(用户设置的日历)
    ln.repeatCalendar = [NSCalendar  currentCalendar];
//    NSString * const NSGregorianCalendar; 公历
//    NSString * const NSChineseCalendar; 农历
//    ln.repeatCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSChineseCalendar];
//  5.设置应用图标右上角的数字
    ln.applicationIconBadgeNumber = 3;
//  6.设置点击推送通知进入界面的时候显示,加载图片
    ln.alertLaunchImage = @"";
//  7 设置通知的音效(只有真机有效)
    local.soundName = UILocalNotificationDefaultSoundName;
//  8 设置一些额外信息
    local.userInfo = @{@"QQ":@"55555",@"info":@"约了没"};

//  iOS8.0 以后新增属性
//  ************************************
//  1.设置区域,进入或离开某个区域的时候触发
//    CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(40.1,106.1);
//    ln.region = [[CLCircularRegion alloc] initWithCenter:coordinate radius:10.0 identifier:@"ab"];
//  2.设置进入或离开某个区域只执行一次
//    ln.regionTriggersOnce = YES;
//  ***************************************

//  iOS8.2 新增属性
//    ln.alertTitle = @"通知标题";


  • 使用应用[UIApplication]调度本地通知
//  让应用调度通知
    [[UIApplication sharedApplication] scheduleLocalNotification:ln];

2. 本地推送通知页面跳转


无论应用是在前台,后台还是已经关闭都能如期接收到本地通知,但是当用户点击通知进入应用的时候,我们需要根据不同情况,进行处理

AppDelegate本地通知代理方法


/**
 *  一旦接收到本地通知就会调用该方法
 *  注意这个方法:应用在前台也会调用
 *  @param application  应用
 *  @param notification 本地通知对象
 */
- (void) application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{

//  当应用在前台时候什么都不做
    if (application.applicationState == UIApplicationStateActive) {
        return;
    }

//  当应用不再前台的时候才去跳转,这样用户体检更好
    UITabBarController *tbVc = (UITabBarController *)application.keyWindow.rootViewController;
    tbVc.selectedIndex = 1;

}

但是当应用已经退出的时候,点击通知进入本应用时候,不在调用application:didReceiveLocalNotification:的代理方法,难道当应用退出后,用户再进入应用我们就不再跳转指定界面了吗?为了更好用户体验,我此时也应该让应用跳转到指定的界面,怎么才能实现这个功能呢? 我们知道当应用程序启动的时候一定会调用application: didFinishLaunchingWithOptions:的代理方法,在这里我们能拿到本地通知信息,也可以跳转相应的界面

//  如果是点击本地通知进来的那么launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]就会有内容
    if(launchOptions[UIApplicationLaunchOptionsLocalNotificationKey])
    {
        //页面跳转
        UITabBarController *tbVc = (UITabBarController *)self.window.rootViewController;
        tbVc.selectedIndex = 1;
    }

一个应用可能要各种不同的通but知,点击不同的通知可以跳转不同界面,这个有该怎么做呢?

  1. 在发送通知时候,设置userInfo属性
//  7.设置应用信息
    ln.userInfo = @{@"pageKey":@"friend"};
  1. 在AppDelegate本地通知代理方法中进行判断

/**
 *  一旦接收到本地通知就会调用该方法
 *  注意这个方法:应用在前台也会调用
 *  @param application  应用
 *  @param notification 本地通知对象
 */
- (void) application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{

//  当应用在前台时候什么都不做
    if (application.applicationState == UIApplicationStateActive) {
        return;
    }
//  当应用不再前台的时候才去跳转,这样用户体检更好
//  获取tabBarController
    UITabBarController *tbVc = (UITabBarController *)self.window.rootViewController;


//  获取用户设置的跳转页
    NSString *page = notification.userInfo[@"pageKey"];
//  如果是朋友圈
    if ([page isEqualToString:@"session"]) {
        tbVc.selectedIndex = 1;
    }else{
//       否则跳转到好友
        tbVc.selectedIndex = 0;
    }
}

  • 测试launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]中的内容
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
//  获取UIApplicationLaunchOptionsLocalNotificationKey对应内容
    id obj = launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
//  获取控制器(注意此时需要通过self.window,通过application.keyWindow无法获取到,因为此时的window还没有成为keyWindow)
    UITabBarController *tbVc = (UITabBarController *)self.window.rootViewController;
//  获取索引为0的控制(注意此时tbVc.selectedViewController为nil)
    UIViewController *vc = tbVc.viewControllers[0];

//  创建一个文本
    UILabel *label = [[UILabel alloc] init];
    label.backgroundColor = [UIColor brownColor];
//  设置text为UIApplicationLaunchOptionsLocalNotificationKey对应的内容
    label.text =  [obj description] ;
    label.frame = CGRectMake(10, 100, 300, 400);
    label.numberOfLines = 0;
//  添加到控制器的View上
    [vc.view addSubview:label];


    return YES;
}

显示结果:


336729BE-B7DA-4B83-9515-E10864F3FADE.png
  • 我们从中可以出他是一个UILocalNotification对象
  • 所以我们取出UILocalNotification对象,剩下的做法与接收到本地通知代理方法中处理相同,所以我们把它提取为一个公用的方法
/**
 *  根据通知跳转到不同页面
 */
- (void) jumpToPageWithLocalNotification:(UILocalNotification *) notification
{
    //  获取tabBarController
    UITabBarController *tbVc = (UITabBarController *)self.window.rootViewController;

    //  获取用户设置的跳转页
    NSString *page = notification.userInfo[@"pageKey"];
    //  如果是朋友圈
    if ([page isEqualToString:@"session"]) {
        tbVc.selectedIndex = 0;
    }else{
        //       否则跳转到好友
        tbVc.selectedIndex = 1;
    }
}
  • 在didReceiveLocalNotification方法中
/**
 *  一旦接收到本地通知就会调用该方法
 *  注意这个方法:应用在前台也会调用
 *  @param application  应用
 *  @param notification 本地通知对象
 */
- (void) application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{

//  当应用在前台时候什么都不做
    if (application.applicationState == UIApplicationStateActive) {
        return;
    }
//  当应用不再前台的时候才去跳转,这样用户体检更好
    [self jumpToPageWithLocalNotification:notification];
}

在didFinishLaunchingWithOptions方法中

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
//  如果是点击本地通知进来的那么launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]就会有内容
    UILocalNotification *notifcation = launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
//  如果存在通知
    if(notifcation){
        [self jumpToPageWithLocalNotification:notifcation];
    }

    return YES;
}```

###3. iOS8的不同点
---
你如果把上面的程序运行在iOS8上,会爆出如下错误

>预习01-本地推送通知[掌握][615:7847] Attempting to schedule a local notification <UIConcreteLocalNotification: 0x7f935182b150>{fire date = Monday, July 13, 2015 at 9:02:25 AM China Standard Time, time zone = (null), repeat interval = 0, repeat count = UILocalNotificationInfiniteRepeatCount, next fire date = Monday, July 13, 2015 at 9:02:25 AM China Standard Time, user info = {
    pageKey = friend;
}} **with an alert but haven't received permission from the user to display alerts**

也就是在iOS8上要发送本地通知需要 **请求用户权限**
如何请求用户权限呢?一般在新版有变化的地方,在头文件中都会有相应的说明,所以点击到scheduleLocalNotification:方法中,看看有没有我们需要信息
![4D110E8B-9D15-4460-B093-6970621F3229.png](http://upload-images.jianshu.io/upload_images/1253942-62c168e5061a56c9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
点击进去,我们看到
![B3F79CF3-8DCE-4E4D-8F0A-3BD39EF08AF1.png](http://upload-images.jianshu.io/upload_images/1253942-6174fc97ba719369.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
意思就是说:在iOS8.0以后,在调度通知之前你需要使用UIApplication的对象方法registerUseNotificationSetting:来请求用户授权.

这种请求权限的代码一般放在didFinishLaunchingWithOptions:方法中,在用户不卸载的情况下,只需要请求一次,下次在运行就不用请求了!

// 1.如果是iOS8请求用户权限
if ([UIDevice currentDevice].systemVersion.doubleValue >= 8.0) {

    /*
     UIUserNotificationType:

     UIUserNotificationTypeBadge   = 1 << 0, // 接收到通知可更改程序的应用图标
     UIUserNotificationTypeSound   = 1 << 1, // 接收到通知可播放声音
     UIUserNotificationTypeAlert   = 1 << 2, // 接收到通知课提示内容
     如果你需要使用多个类型,可以使用 "|" 来连接
     */

// 向用户请求通知权限
// categories暂时传入nil
UIUserNotificationSettings *setting = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert categories:nil];

    [application registerUserNotificationSettings:setting];
}
运行程序

![21E244DC-087A-4679-9D62-BFDEC648672C.png](http://upload-images.jianshu.io/upload_images/1253942-ee6ddc53a049ddb5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
测试点击通知,进入应用,也没问题

--------
接下来,我们说说-[UIUserNotificationSettings settingsForTypes:categories:] 中的 **categories**

* categories可以让我们发送通知之前预定义一些通知也就是通知上可以显示按钮,他需要是一个装有UIUserNotificationCategory类的对象的NSSet的对象.
但是官方推荐我们使用它的子类UIMutableUserNotificationCategory,来动态的添加通知的行为按钮,iOS8支持前台和后台的两种行为.
* 通知Action按钮以长条展示如图
![notifcation_action_button.gif](http://upload-images.jianshu.io/upload_images/1253942-22b7dbce7618a358.gif?imageMogr2/auto-orient/strip)
* 通知Action按钮以AlertView展示如图
![notification_action_allertview.gif](http://upload-images.jianshu.io/upload_images/1253942-4a6cf2c9fda8767c.gif?imageMogr2/auto-orient/strip)

1. 注册分类,并在分类中添加不同的行为
由于注册用户通知设置代码量比较大我们实现一个新的方法registerUserNotification

  • (void) registerUserNotification
    {
    // 向用户请求通知权限
    /*
    UIUserNotificationType:用户通知的类型

    UIUserNotificationTypeBadge = 1 << 0, // 接收到通知可更改程序的应用图标
    UIUserNotificationTypeSound = 1 << 1, // 接收到通知可播放声音
    UIUserNotificationTypeAlert = 1 << 2, // 接收到通知课提示内容
    如果你需要使用多个类型,可以使用 "|" 来连接
    */
    // 1.设置用户通知权限类型
    UIUserNotificationType types = UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert;

// 2.创建通知的行为按钮

// 2.1创建第一个行为
UIMutableUserNotificationAction *action1 = [[UIMutableUserNotificationAction alloc] init];
// 2.1.1 设置行为的唯一标示
action1.identifier = UIMutableUserNotificationActionBackground;
// 2.1.2 设置通知按钮的的标题
action1.title = @"后台";
// 以什么样模式运行应用
// UIUserNotificationActivationModeForeground, // 当应用在前台的时候触发
// UIUserNotificationActivationModeBackground // 即使应用不在前台也触发
action1.activationMode = UIUserNotificationActivationModeBackground;
// 2.1.3 是否只有锁屏的锁屏状态下才能显示
action1.authenticationRequired = NO;
// 2.1.4 按钮的性质
action1.destructive = NO;

// 2.1创建第一个行为
UIMutableUserNotificationAction *action2 = [[UIMutableUserNotificationAction alloc] init];
// 2.1.1 设置行为的唯一标示
action2.identifier =UIMutableUserNotificationActionForeground;
// 2.1.2 设置通知按钮的的标题
action2.title = @"前台";
// 以什么样模式运行应用
// UIUserNotificationActivationModeForeground, // 当应用在前台的时候触发
// UIUserNotificationActivationModeBackground // 即使应用不在前台也触发
action2.activationMode = UIUserNotificationActivationModeForeground;
// 2.1.3 用户必须输入密码才能执行
action2.authenticationRequired = YES;
// 2.1.4 按钮的性质(没有效果)
action2.destructive = YES;

// 3.创建用户通知分类
UIMutableUserNotificationCategory *category = [[UIMutableUserNotificationCategory alloc] init];
// 3.1 设置类别的唯一标识
category.identifier = @"myCategory";
// 3.2 设置通知的按钮
// Context:
// UIUserNotificationActionContextDefault, //默认上下文(情景)下的英文(通常都是)
// UIUserNotificationActionContextMinimal //通知内容区域受限情况下内容
[category setActions:@[action1,action2] forContext:UIUserNotificationActionContextDefault];

// 4.创建用户通知的设置信息
UIUserNotificationSettings *setting = [UIUserNotificationSettings settingsForTypes:types categories:[NSSet setWithObject:category]];

// 注册设置
[[UIApplication sharedApplication] registerUserNotificationSettings:setting];
}


* 在发送本地推送通知时候指定通知的分类标示

// 9.设置通知的类别
ln.category = @"myCategory";


* 监听点击通知按钮的行为,在AppDelegate中实现监听通知按钮点击方法

/**

  • 当用户点击通知上定制的按钮执行的行为(注意:不点击行为按钮,不会进入该方法)
  • @param application 应用
  • @param identifier 行为标识符
  • @param notification 本地通知
  • @param completionHandler 完成回调
    */
  • (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString )identifier forLocalNotification:(UILocalNotification )notification completionHandler:(void (^)())completionHandler
    {
    // 处理不同行为
    if ([identifier isEqualToString:UIMutableUserNotificationActionBackground]) {
    NSLog(@"后台运行程序");
    }else if ([identifier isEqualToString:UIMutableUserNotificationActionForeground]){
    NSLog(@"前台运行程序");
    }else{
    NSLog(@"其他");
    }
    /

    You should call the completion handler as soon as you've finished handling the action.
    当任务处理完毕时候,你应该尽快的调用completion的block.
    */

// 在当任务完成的时候,调用任务完成的block
completionHandler();
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • 应用程序必须进行适当配置,才可以接受本地或远程通知。配置过程在iOS和OS X略有不同,但基本原理是相同的。在启动...
    shenzhenboy阅读 1,376评论 1 2
  • 这两天在做一个日程提醒功能,用UILocalNotification做日程的提醒功能,相关知识如下: 一、本地通知...
    七维树阅读 1,150评论 1 1
  • 极光推送: 1.JPush当前版本是1.8.2,其SDK的开发除了正常的功能完善和扩展外也紧随苹果官方的步伐,SD...
    Isspace阅读 6,694评论 10 16
  • 项目中用到了本地通知功能,便到网上搜罗了一翻,各式各样的说法,经过自己的验证,整理如下 首先要说明的是IOS最多允...
    CrazySteven阅读 640评论 3 2
  • 躁动的夏日仿佛一夜之间来到了大地上,即使隔三差五的还来点小寒流,但不容置疑,它真的来了。刚刚结束或者说放弃了一段本...
    季鸿宇阅读 204评论 0 0