NSNotificationCenter基本介绍:
要在代码中的两个不相关的模块中传递消息时,通知机制是非常好的工具。通知机制广播消息,当消息内容丰富而且无需指望接收者一定要关注的话这一招特别有用。
通知可以用来发送任意消息,甚至可以包含一个 userInfo
字典。你也可以继承 NSNotification
写一个自己的通知类来自定义行为。通知的独特之处在于,发送者和接收者不需要相互知道对方,所以通知可以被用来在不同的相隔很远的模块之间传递消息。这就意味着这种消息传递是单向的,我们不能回复一个通知。NSNotificationCenter(通知)是一个很好的选择。
作用:NSNotificationCenter是专门供程序中不同类间的消息通信而设置的.
注册通知:即要在什么地方接受消息
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mytest:) name:@" mytest" object:nil];
参数:
addObserver: 添加观察者;
selector: 收到通知所执行的方法;
name: 通知的名字,也是通知的唯一标示,编译器就通过这个找到通知的。(自定义)
发送通知:调用观察者处的方法。
[[NSNotificationCenter defaultCenter] postNotificationName:@"mytest" object:nil];
参数:
postNotificationName:通知的名字,也是通知的唯一标示,编译器就通过这个找到通知的
object:传递的参数填写nil表示接收所有发送者的通知。
注册执行方法:
-(void)mytest:(NSNotification*) notification {
id obj = [notification object];//获取到传递的对象
}
使用步骤:
1:首先注册消息通知. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mytest:) name:@"mytest" object:nil];
2:实现消息通知的方法. -(void) mytest:(NSNotification*) notification{执行的操作}
3:在需要的地方进行发送通知. [[NSNotificationCenter defaultCenter] postNotificationName:@"mytest" object:nil];一旦发送通知就会执行实现消息通知的方法.
4:最后在 dealloc中去除接受者 - (void)removeObserver:(id)observer
一:使用NSNotificationCenter监听系统组件的通知
@end
@implementation FKViewController
- (void)viewDidLoad{
[superviewDidLoad];
//监听UIApplication的UIApplicationDidFinishLaunchingNotification通知
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(launch:) name:UIApplicationDidFinishLaunchingNotification
object:[UIApplicationsharedApplication]];
//监听UIApplication的UIApplicationDidEnterBackgroundNotification通知
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(back:) name:UIApplicationDidEnterBackgroundNotification
object:[UIApplicationsharedApplication]];
//监听UIApplication的UIApplicationWillEnterForegroundNotification通知
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(fore:) name:UIApplicationWillEnterForegroundNotification
object:[UIApplicationsharedApplication]];
}
进入不同的状态,显示不同的信息。当UIApplication向NSNotificationCenter发送通知时,该视图控制器中对应的监听方法会被执行。
-(void)launch:(NSNotification*) notification{
self.showLabel.text = [NSStringstringWithFormat:@"应用程序加载完成!"];
}
-(void)back:(NSNotification*) notification{
self.showLabel.text = [NSStringstringWithFormat:@"%@\n应用程序进入后台!",self.showLabel.text];
}
- (void) fore:(NSNotification*) notification{
self.showLabel.text = [NSStringstringWithFormat:@"%@\n应用程序进入前台!",self.showLabel.text];
}
@end
二:使用NSNotificationCenter监听自定义通知
下面是使用异步操作来模拟执行一个耗时任务,并在界面上使用UIProgressView显示耗时任务的进度。如果需要让UIProgressView与耗时任务以松耦合的方式进行通信,则可以通过NSNotificationCenter来实现---每当耗时操作任务完成百分比发生改变时,程序向NSNotificationCenter发生通知,而程序本身使用视图控制器作为通知监听器,当视图控制器监听到进度改变的通知时,视图控制器类修改UIProgressView的进度即可。
#import <UIKit/UIKit.h>
@interface FKViewController :UIViewController
@property (strong,nonatomic)IBOutletUIProgressView *prog;
@property (strong,nonatomic)IBOutletUIButton *bn;
- (IBAction)start:(id)sender;
@end
#import "FKViewController.h"
#define PROGRESS_CHANGED @"down_progress_changed"
@interfaceFKViewController (){
NSNotificationCenter* nc;
NSOperationQueue* queue;
}
@end
@implementation FKViewController
- (void)viewDidLoad{
[superviewDidLoad];
nc = [NSNotificationCenter defaultCenter];
queue = [[NSOperationQueuealloc]init];
//设置该队列最多支持10条并发线程
queue.maxConcurrentOperationCount =10;
//使用视图控制器监听任何对象发出的PROGRESS_CHANGED通知
[nc addObserver:selfselector:@selector(update:) name:PROGRESS_CHANGED object:nil];
}
-(IBAction)start:(id)sender {
__block int progStatus =0;
[sender setEnabled:NO];
//以传入的代码块作为执行体,创建NSOperation
NSBlockOperation* operation = [NSBlockOperation blockOperationWithBlock:^{
for (int i =0 ; i <100; i++){
//暂停0.5秒模拟耗时任务
[NSThread sleepForTimeInterval:0.5];
//创建NSNotification,并指定userInfo信息
NSNotification* noti = [NSNotification notificationWithName:PROGRESS_CHANGED object:nil userInfo: [NSDictionarydictionaryWithObjectsAndKeys:
[NSNumbernumberWithInt:++progStatus],@"prog" , nil]];
//发送通知
[nc postNotification:noti];
}
}];
//将NSOperation添加给NSOperationQueue
[queue addOperation:operation];
}
-(void)update:(NSNotification*) noti{
//通过userInfo属性获取耗时任务的进度信息
NSNumber* progStatus = noti.userInfo[@"prog"];
dispatch_async(dispatch_get_main_queue(), ^{
self.prog.progress = progStatus.intValue / 100.0;
//当任务执行进度执行到100时,启用按钮
if (progStatus.intValue ==100) {
[self.bn setEnabled:YES];
}
});
}
@end
本地通知和远程通知都可以向不在后台运行的应用发送消息,这种消息及可能是即将发生的事件,也可能是服务器的新数据。不管是本地通知还是远程推送通知,它们在程序界面显示的效果是相同,都可能显示为一段警告信息或者应用图标上的徽标。
当用户点击本地通知或者远程推送通知时,可以启动相应的应用程序来查看详情,也可以选择忽略通知,此时应用程序将不会被激活。
本地通知和远程通知的基本目的都是让应用程序能够通知用户某些事情,而且不需要应用在前台运行。二者区别在于:本地通知由本应用负责调用,只能从当前设备上的iOS发出;而远程推送通知由远程服务器上的程序(可油任意语言编写)发送至Apple Push Notification service (APNS),再由APNS把消息推送至设备上对应的程序。
本定通知(仅在iOS中有效)适用于基于时间的程序,包括简单的日历或者to-do列表类型的应用程序。那些在有限时间内运行的后台程序(iOS系统许可的)也能够接收到本地通知。例如:需要从服务端获取数据的应用,可以在后台运行并查询服务端最新的数据,如果有消息或者数据需要用户更新或者下载,程序可以立即发送一个本地通知来通知用户。
本地通知是一个UILocalNotification对象,它有如下常用属性:
// timer-based scheduling 指定通知将在什么时候发生
@property(nonatomic,copy)NSDate *fireDate;
// 设置本地通知重复发送的时间间隔
@property(nonatomic)NSCalendarUnit repeatInterval; // 0 means don't repeat
// alerts 设置本地通知的消息体
@property(nonatomic,copy)NSString *alertBody; // defaults to nil. pass a string or localized string key to show an alert
//设置是否显示Action
@property(nonatomic)BOOL hasAction; // defaults to YES. pass NO to hide launching button/slider
//设置当设备处于锁屏状态时,显示通知的警告框下方的title
@property(nonatomic,copy)NSString *alertAction; // used in UIAlert button or 'slide to unlock...' slider in place of unlock
//当用户通过该通知启动对应的应用时,该属性设置为加载图片
@property(nonatomic,copy)NSString *alertLaunchImage; // used as the launch image (UILaunchImageFile) when launch button is tapped
// sound 设置通知声音
@property(nonatomic,copy)NSString *soundName; // name of resource in app's bundle to play or UILocalNotificationDefaultSoundName
// badge 设置显示在应用程序上红色徽标中的数字
@property(nonatomic)NSInteger applicationIconBadgeNumber; // 0 means no change. defaults to 0
// user info 设置该通知携带的附加信息。
@property(nonatomic,copy)NSDictionary *userInfo; // throws if contains non-property list types
创建了UILocalNotification对象之后,接下来就可以通过UIApplication的如下两个方法发送通知。
@property(nonatomic,copy)NSArray *scheduledLocalNotificationsNS_AVAILABLE_IOS(4_0);//该方法指定调度通知。通知将会于fireDate指定的时间触发,而且会按照repeatInterval时间间隔重复触发。
- (void)presentLocalNotificationNow:(UILocalNotification *)notificationNS_AVAILABLE_IOS(4_0);//该方法指定立即发送通知。该方法忽略UILocalNotification的fireDate属性。
在iOS应用中发送本地通知的步骤很简单,如下:
1) 创建UILocalNotification对象
2) 设置UILocalNotification属性
3) 调用UIApplication的方法发送或调用通知
4) 如果希望应用程序在前台运行时可以对通知进行相应的处理,则需要重写应用程序委托类的- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification方法。
5) 当应用需要取消本地通知的时候,可调用UIApplication的-(void)cancelLocalNotification:(UILocalNotification *)notification NS_AVAILABLE_IOS(4_0);方法取消指定通知,或者调用-(void)cancelAllLocalNotificationsNS_AVAILABLE_IOS(4_0);取消所有的通知。
程序例子: 该例子在界面上显示一个UISwitch控件,当该控件处于打开状态时,系统会不断地发送本地通知,当控件处于关闭状态时,系统取消所有的本地通知。
FKAppDelegate.m文件中
#import "FKAppDelegate.h"
@implementation FKAppDelegate
// 只有当应用在前台时,该方法才会被调用
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification*)notification{
//如果应用程序在前台,将应用程序图标上红色徽标中数字设为0
application.applicationIconBadgeNumber =0;
//使用UIAlertView显示本地通知的信息,notification.alertBody需要通知的消息
[[[UIAlertViewalloc]initWithTitle:@"收到通知" message:notification.alertBody delegate:nil cancelButtonTitle:@"确定"otherButtonTitles:nil]show];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
注意下面这段代码:如果没有会报错,with a badge number but haven't received permission from the user to badgethe application 。一切都是iOS8捣的鬼。您如果把模拟器换成iOS7.1或者更早的,就不会有这个问题。而现在在iOS8中要实现badge、alert和sound等都需要用户同意才能,因为这些都算做Notification“通知”,为了防止有些应用动不动给用户发送“通知”骚扰用户,所以在iOS8时,要“通知”必须要用户同意才行。
解决思路:我们判断一下,如果系统版本大于等于8.0的话,我们就在用户打开应用的时候弹出一个框框提示说我们要发送通知给你,你同意不?如果用户同意,那么我们就可以了。而如果系统版本小于8.0的话,因为默认是可以的,所以我们不需要做任何事情。http://stackoverflow.com/questions/25973364/attempting-to-badge-the-application-icon-but-havent-received-permission-from-th
float sysVersion=[[UIDevicecurrentDevice]systemVersion].floatValue;
if (sysVersion>=8.0) {
UIUserNotificationType type=UIUserNotificationTypeBadge |UIUserNotificationTypeAlert | UIUserNotificationTypeSound;
UIUserNotificationSettings *setting=[UIUserNotificationSettingssettingsForTypes:typecategories:nil];
[[UIApplicationsharedApplication]registerUserNotificationSettings:setting];
}
// Override point for customization after application launch.
returnYES;
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
//应用程序再次进入前台时,将应用程序徽标设置0
application.applicationIconBadgeNumber =0;
}
@end
#import <UIKit/UIKit.h>
@interface FKViewController :UIViewController
- (IBAction)changed:(id)sender;//控制开关是否开启
@end
#import "FKViewController.h"
@interfaceFKViewController(){
UIApplication *app;
}
@end
@implementation FKViewController
- (void)viewDidLoad
{
[superviewDidLoad];
app = [UIApplicationsharedApplication];
}
- (IBAction)changed:(id)sender{
UISwitch *sw = (UISwitch*) sender;
if (sw.on){
//创建一个本地通知
UILocalNotification *notification = [[UILocalNotification alloc]init];
//设置通知的触发时间
notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:10];;
//设置通知的时区
notification.timeZone = [NSTimeZone defaultTimeZone];
//设置通知的重复发送的事件间隔
notification.repeatInterval =kCFCalendarUnitMinute;
//设置通知的声音
notification.soundName =@"gu.mp3";
//设置当设备处于锁屏状态时,显示通知的警告框下方的title
notification.alertAction =@"打开";
//设置通知是否可显示Action
notification.hasAction =YES;
//设置通过通知加载应用时显示的图片
notification.alertLaunchImage =@"logo.png";
//设置通知内容
notification.alertBody =@"轮到你下棋了,赶快走棋!";
//设置显示在应用程序上红色徽标中的数字
notification.applicationIconBadgeNumber =1;
//设置userinfo,用于携带额外的附加信息。
NSDictionary *info =@{@"fkjava.org":@"key"};
notification.userInfo = info;
//调度通知
[app scheduleLocalNotification:notification]; //①
}else{
//获取所有处于调度中本地通知数组
NSArray *localArray = [app scheduledLocalNotifications];
if(localArray){
for (UILocalNotification *notiin localArray){
NSDictionary *dict = noti.userInfo;
if (dict){
//如果找到要取消的通知
NSString *inKey = [dict objectForKey:@"key"];
if ([inKeyisEqualToString:@"fkjava.org"]){
//取消调度该通知
[app cancelLocalNotification:noti]; //②
}
}
}
}
}
}
@end
Attempting to schedule a local notification ,with an alert but haven't received permission from the user to display alerts,with a sound but haven't received permission from the user to play sounds
原因在于iOS8系统变更了注册方法,需要在ApplicationDelegate里面注册通知才可以。
if(UIApplication instancesRespondToSelector:@selector(registerUserNotificationSettings:)){
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
}