iOS本地推送
1. 推送通知简介
1.1: 这里说的推送通知跟NSNotification有所区别
- NSNotification是抽象的,不可见的
- 推送通知是可见的(能用肉眼看到)
1.2: 作用:让不在前台(后台或者关闭)的APP知道APP内部发生的事情
推送1.3: 通知的分类
- 本地推送通知(Local Notification)
- 概念:由APP本身给应用程序推送消息,不需要服务器的支持
- 常见场景:记账软件定时提醒记账/番茄工作法中提醒你时间等等
- 注意:不是非常常用.
- 远程推送通知(Remote Notification)
- 概念:由服务器推送消息给用户,需要服务器的支持
- 常见场景:微信提醒新消息/淘宝提醒有新活动/视频软件提供您有最新电影
- 注意:非常常用.但是如果仅仅是给用户提醒,客户端(你)做的事情就非常简单.
1.4: 推送通知的呈现样式(了解)
- 在屏幕顶部显示一块横幅
- 在屏幕中间弹出一个UIAlertView
- 锁屏界面也可以显示
收到通知时,同时播放音效.
收到通知时,改变APP图标上的数字
二:本地通知的发送实现
2.1 本地通知的介绍
- 直接由应用程序(程序中写入对应代码)给用户发出通知
- 本地通知需要用到一个重要的类:UILocalNotification
- 本地通知的实现步骤
- 创建本地通知
- 设置本地通知要发出的内容等信息
- 发出时间
- 发出内容
- 播放的音效
- 调度本地通知
2.2 实现本地通知
#######1.注册通知
- iOS8之后,如果想要发出通知(无论本地还是远程),必须先进行注册.(iOS8之前不需要)
- 通常是在
didFinishLaunchingWithOptions
中进行注册 - 代码如下:
if ([[UIDevice currentDevice].systemVersion doubleValue] >= 8.0 ) {
//设置本地通知
UIUserNotificationSettings *setting = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert categories:nil];
[application registerUserNotificationSettings:setting];
}
#######2. 创建并且发出通知
- 创建本地通知和设置本地通知的内容
- (void)scheduleNotificationWithItem:(ToDoItem *)item interval:(int)minutesBefore {
NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
NSDateComponents *dateComps = [[NSDateComponents alloc] init];
[dateComps setDay:item.day];
[dateComps setMonth:item.month];
[dateComps setYear:item.year];
[dateComps setHour:item.hour];
[dateComps setMinute:item.minute];
NSDate *itemDate = [calendar dateFromComponents:dateComps];
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif == nil) return;
localNotif.fireDate = [itemDate dateByAddingTimeIntervalInterval:-(minutesBefore*60)];
localNotif.timeZone = [NSTimeZone defaultTimeZone];
localNotif.alertBody = [NSString stringWithFormat:NSLocalizedString(@"%@ in %i minutes.", nil),
item.eventName, minutesBefore];
localNotif.alertAction = NSLocalizedString(@"View Details", nil);
localNotif.alertTitle = NSLocalizedString(@"Item Due", nil);
localNotif.soundName = UILocalNotificationDefaultSoundName;
localNotif.applicationIconBadgeNumber = 1;
NSDictionary *infoDict = [NSDictionary dictionaryWithObject:item.eventName forKey:ToDoItemKey];
localNotif.userInfo = infoDict;
// 设置好本地推送后必须调用此方法启动此推送
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
}
#######3.取消本地推送的方法
// 取消某一个本地推送
[[UIApplication sharedApplication] cancelLocalNotification:notification];
// 取消所有的本地推送
[[UIApplication sharedApplication] cancelAllLocalNotifications];
#######4, 通知的其他属性
@property(nullable, nonatomic,copy) NSDate *fireDate;
@property(nullable, nonatomic,copy) NSTimeZone *timeZone 时区
@property(nonatomic) NSCalendarUnit repeatInterval; 通知的重复间隔
@property(nullable, nonatomic,copy) NSCalendar *repeatCalendar; 重复日期
@property(nullable, nonatomic,copy) CLRegion *region 区域:当进入该区域时,就会发出一个通知
@property(nonatomic,assign) BOOL regionTriggersOnce YES:进入某一个区域只会发出一次通知.NO:每次进入该区域都会发出通知
@property(nullable, nonatomic,copy) NSString *alertBody;
@property(nonatomic) BOOL hasAction; 用于决定alertAction是否生效
@property(nullable, nonatomic,copy) NSString *alertAction; 锁屏界面滑块下显示的文字
@property(nullable, nonatomic,copy) NSString *alertLaunchImage; 不需要设置
@property(nullable, nonatomic,copy) NSString *alertTitle 通知中心的标题
// sound
@property(nullable, nonatomic,copy) NSString *soundName; 设置通知发出时音效 UILocalNotificationDefaultSoundName
// badge
@property(nonatomic) NSInteger applicationIconBadgeNumber; 应用程序右上角的数字
// user info
@property(nullable, nonatomic,copy) NSDictionary *userInfo; 额外的信息
三: 监听本地通知的点击
3.1.为什么要监听本地通知的点击?
- 1.通知点击之后会发生什么事情?
不管应用程序出于后台还是被杀死,点击通知都可以打开应用程序 - 2.什么情况下需要监听用户点击了通知(不常用)
比如:当用点击通知时,进入到某一个固定界面
3.2 如何监听本地通知的点击
-
1: 应用程序分很多种状态
- 在前台: 如果在前台不需要进行页面跳转
- 在后台: 点击应用时进行页面的跳转
- 被杀死: 点击应用打开应用时,进行页面的跳转
-
2:
application: didReceiveLocalNotification:
如果App处于Background状态时,只有用户点击了通知消息时才会调用该方法;如果App处于Foreground状态,会直接调用该方法。
// 当收到一个本地通知时会调用该方法
/*
UIApplicationStateActive, 在前台
UIApplicationStateInactive, 进入前台
UIApplicationStateBackground 在后台
*/
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
if (application.applicationState == UIApplicationStateActive) return;
// 在进入前台时,会执行该方法
// NSLog(@"跳转到一个固定的界面中");
UILabel *label = [[UILabel alloc] init];
label.frame = CGRectMake(50, 50, 200, 200);
label.backgroundColor = [UIColor redColor];
[self.window.rootViewController.view addSubview:label];
[self jumpToVc];
}
- (void)jumpToVc
{
NSLog(@"跳转到一个界面中");
}
- 3:
application: didFinishLaunchingWithOptions:
此方法在程序第一次启动是调用,也就是说App从Terminate状态进入Foreground状态的时候,根据方法内代码判断是否有推送消息。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([[UIDevice currentDevice].systemVersion doubleValue] >= 8.0) {
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert categories:nil];
[application registerUserNotificationSettings:settings];
}
// launchOptions:
// 如果是正常启动程序,该参数为nil.
// 如果是通过别的方式打开我的应用程序,则launchOptions有值
if (launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]) {
UILabel *label = [[UILabel alloc] init];
label.frame = CGRectMake(50, 50, 200, 400);
label.backgroundColor = [UIColor redColor];
label.text = launchOptions.description;
label.numberOfLines = 0;
label.font = [UIFont systemFontOfSize:14.0];
[self.window.rootViewController.view addSubview:label];
[self jumpToVc];
}
return YES;
}
四: 带有定位的本地推送
iOS8之后,本地推送可以创建带有定位的本地推送,当用户进入这一指定的区域的时候就会发送此推送。并且可以指定此消息是只发送一次,还是每次用户进入此区域的时候都发送此推送。设置此属性对应的属性字段为:regionTriggersOnce。
#######4.1 开启定位服务
注册带有定位的推送必要要求用户开启定位功能,授权使用定位服务
// 获得授权去追踪用户的位置
CLLocationManager *locManager = [[CLLocationManager alloc] init];
locManager.delegate = self;
[locManager requestWhenInUseAuthorization];
当你第一次请求使用授权定位服务的时候,系统会询问用户是同意还是拒绝。为了提醒用户,系统会显示一些在Info.plist中NSLocationWhenInUseUsageDescription
此key值对应的额外信息。使用定位服务此key值一定要配置。只有用户同意了此App访问他的位置信息,此App才会接收到回到方法。
#######4.2 处理定位回调
首先要检查用户对定位服务的授权状态,只有用户授权了可以访问用户的位置信息,才会回调以下方法
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
//检查状态判断App是否授权
if (status == kCLAuthorizationStatusAuthorizedWhenInUse) {
// 设置通知
[self startShowingLocationNotifications];
}
}
// 设置通知
- (void)startShowingLocationNotifications {
UILocalNotification *locNotification = [[UILocalNotification alloc] init];
locNotification.alertBody = @"You have arrived!";
locNotification.regionTriggersOnce = YES;
locNotification.region = [[CLCircularRegion alloc] initWithCenter:LOC_COORDINATE radius:LOC_RADIUS identifier:LOC_IDENTIFIER];
[[UIApplication sharedApplication] scheduleLocalNotification:locNotification];
}
// 收到本地通知的时候,回调此方法,处理通知
- (void)application:(UIApplication *)application didReceiveLocalNotification: (UILocalNotification *)notification {
if (notification.region) {
[self tellUserArrivedAtRegion:region];
}
}
五: 本地推送使用注意(本地推送的坑)
本地推送的准确率和到达率都是秒杀远程推送的,但是本地推送也有一些问题,处理不善的话很容易让用户误解的。本地推送无论是在App处于什么状态,当有消息的时候,在手机下拉的Notifications中都是会有显示的。当App不是处于Foreground状态的时候,都是会有banner通知的,但是当App处于Foreground状态时,通知还是会一样发出,而且手机下拉Notifications中会有磁条通知,但是banner并不会出现。这样就会引起一个时间错觉问题。而且本地推送手机下拉Notifications中的消息显示,当用户打开App的时候不会消失的啊。只有用户点击此条消息或者直接给清除掉才会消息的啊。做此功能的时候,一直被说本地推送不准啊,我都同步数据了,为什么还让同步,经过几番测试,最终发现原因出于此,不知道算不算系统的一个bug。
在同步数据之后再设置[UIApplication sharedApplication].applicationIconBadgeNumber = 0可以清除通知栏里面的推送消息。