欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

iOS開發(fā)系列--通知與消息機(jī)制詳解

 更新時(shí)間:2016年11月08日 15:22:49   作者:KenshinCui  
這篇文章主要介紹了iOS開發(fā)系列--通知與消息機(jī)制詳解,有需要的同學(xué)可以了解一下。

概述
在多數(shù)移動(dòng)應(yīng)用中任何時(shí)候都只能有一個(gè)應(yīng)用程序處于活躍狀態(tài),如果其他應(yīng)用此刻發(fā)生了一些用戶感興趣的那么通過通知機(jī)制就可以告訴用戶此時(shí)發(fā)生的事情。iOS中通知機(jī)制又叫消息機(jī)制,其包括兩類:一類是本地通知;另一類是推送通知,也叫遠(yuǎn)程通知。兩種通知在iOS中的表現(xiàn)一致,可以通過橫幅或者彈出提醒兩種形式告訴用戶,并且點(diǎn)擊通知可以會(huì)打開應(yīng)用程序,但是實(shí)現(xiàn)原理卻完全不同。今天就和大家一塊去看一下如何在iOS中實(shí)現(xiàn)這兩種機(jī)制,并且在文章后面會(huì)補(bǔ)充通知中心的內(nèi)容避免初學(xué)者對(duì)兩種概念的混淆。

本地通知

本地通知是由本地應(yīng)用觸發(fā)的,它是基于時(shí)間行為的一種通知形式,例如鬧鐘定時(shí)、待辦事項(xiàng)提醒,又或者一個(gè)應(yīng)用在一段時(shí)候后不使用通常會(huì)提示用戶使用此應(yīng)用等都是本地通知。創(chuàng)建一個(gè)本地通知通常分為以下幾個(gè)步驟:

  • 創(chuàng)建UILocalNotification。
  • 設(shè)置處理通知的時(shí)間fireDate。
  • 配置通知的內(nèi)容:通知主體、通知聲音、圖標(biāo)數(shù)字等。
  • 配置通知傳遞的自定義數(shù)據(jù)參數(shù)userInfo(這一步可選)。
  • 調(diào)用通知,可以使用scheduleLocalNotification:按計(jì)劃調(diào)度一個(gè)通知,也可以使用presentLocalNotificationNow立即調(diào)用通知。

下面就以一個(gè)程序更新后用戶長期沒有使用的提醒為例對(duì)本地通知做一個(gè)簡(jiǎn)單的了解。在這個(gè)過程中并沒有牽扯太多的界面操作,所有的邏輯都在AppDelegate中:進(jìn)入應(yīng)用后如果沒有注冊(cè)通知,需要首先注冊(cè)通知請(qǐng)求用戶允許通知;一旦調(diào)用完注冊(cè)方法,無論用戶是否選擇允許通知此刻都會(huì)調(diào)用應(yīng)用程序的- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings代理方法,在這個(gè)方法中根據(jù)用戶的選擇:如果是允許通知?jiǎng)t會(huì)按照前面的步驟創(chuàng)建通知并在一定時(shí)間后執(zhí)行。

AppDelegate.m

//
// AppDelegate.m
// LocalNotification
//
// Created by Kenshin Cui on 14/03/28.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "AppDelegate.h"
#import "KCMainViewController.h"

@interface AppDelegate ()

@end

@implementation AppDelegate

#pragma mark - 應(yīng)用代理方法
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  
  _window=[[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
  
  _window.backgroundColor =[UIColor colorWithRed:249/255.0 green:249/255.0 blue:249/255.0 alpha:1];
  
  //設(shè)置全局導(dǎo)航條風(fēng)格和顏色
  [[UINavigationBar appearance] setBarTintColor:[UIColor colorWithRed:23/255.0 green:180/255.0 blue:237/255.0 alpha:1]];
  [[UINavigationBar appearance] setBarStyle:UIBarStyleBlack];
  
  KCMainViewController *mainController=[[KCMainViewController alloc]init];
  _window.rootViewController=mainController;
  
  [_window makeKeyAndVisible];

  //如果已經(jīng)獲得發(fā)送通知的授權(quán)則創(chuàng)建本地通知,否則請(qǐng)求授權(quán)(注意:如果不請(qǐng)求授權(quán)在設(shè)置中是沒有對(duì)應(yīng)的通知設(shè)置項(xiàng)的,也就是說如果從來沒有發(fā)送過請(qǐng)求,即使通過設(shè)置也打不開消息允許設(shè)置)
  if ([[UIApplication sharedApplication]currentUserNotificationSettings].types!=UIUserNotificationTypeNone) {
    [self addLocalNotification];
  }else{
    [[UIApplication sharedApplication]registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
  }
  
  return YES;
}

#pragma mark 調(diào)用過用戶注冊(cè)通知方法之后執(zhí)行(也就是調(diào)用完registerUserNotificationSettings:方法之后執(zhí)行)
-(void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings{
  if (notificationSettings.types!=UIUserNotificationTypeNone) {
    [self addLocalNotification];
  }
}

#pragma mark 進(jìn)入前臺(tái)后設(shè)置消息信息
-(void)applicationWillEnterForeground:(UIApplication *)application{
  [[UIApplication sharedApplication]setApplicationIconBadgeNumber:0];//進(jìn)入前臺(tái)取消應(yīng)用消息圖標(biāo)
}

#pragma mark - 私有方法
#pragma mark 添加本地通知
-(void)addLocalNotification{
  
  //定義本地通知對(duì)象
  UILocalNotification *notification=[[UILocalNotification alloc]init];
  //設(shè)置調(diào)用時(shí)間
  notification.fireDate=[NSDate dateWithTimeIntervalSinceNow:10.0];//通知觸發(fā)的時(shí)間,10s以后
  notification.repeatInterval=2;//通知重復(fù)次數(shù)
  //notification.repeatCalendar=[NSCalendar currentCalendar];//當(dāng)前日歷,使用前最好設(shè)置時(shí)區(qū)等信息以便能夠自動(dòng)同步時(shí)間
  
  //設(shè)置通知屬性
  notification.alertBody=@"最近添加了諸多有趣的特性,是否立即體驗(yàn)?"; //通知主體
  notification.applicationIconBadgeNumber=1;//應(yīng)用程序圖標(biāo)右上角顯示的消息數(shù)
  notification.alertAction=@"打開應(yīng)用"; //待機(jī)界面的滑動(dòng)動(dòng)作提示
  notification.alertLaunchImage=@"Default";//通過點(diǎn)擊通知打開應(yīng)用時(shí)的啟動(dòng)圖片,這里使用程序啟動(dòng)圖片
  //notification.soundName=UILocalNotificationDefaultSoundName;//收到通知時(shí)播放的聲音,默認(rèn)消息聲音
  notification.soundName=@"msg.caf";//通知聲音(需要真機(jī)才能聽到聲音)
  
  //設(shè)置用戶信息
  notification.userInfo=@{@"id":@1,@"user":@"Kenshin Cui"};//綁定到通知上的其他附加信息
  
  //調(diào)用通知
  [[UIApplication sharedApplication] scheduleLocalNotification:notification];
}

#pragma mark 移除本地通知,在不需要此通知時(shí)記得移除
-(void)removeNotification{
  [[UIApplication sharedApplication] cancelAllLocalNotifications];
}
@end

請(qǐng)求獲得用戶允許通知的效果:

應(yīng)用退出到后彈出通知的效果:

鎖屏狀態(tài)下的通知效果(從這個(gè)界面可以看到alertAction配置為“打開應(yīng)用”):

注意:

  • 在使用通知之前必須注冊(cè)通知類型,如果用戶不允許應(yīng)用程序發(fā)送通知,則以后就無法發(fā)送通知,除非用戶手動(dòng)到iOS設(shè)置中打開通知。
  • 本地通知是有操作系統(tǒng)統(tǒng)一調(diào)度的,只有在應(yīng)用退出到后臺(tái)或者關(guān)閉才能收到通知。(注意:這一點(diǎn)對(duì)于后面的推送通知也是完全適用的。 )
  • 通知的聲音是由iOS系統(tǒng)播放的,格式必須是Linear PCM、MA4(IMA/ADPCM)、µLaw、aLaw中的一種,并且播放時(shí)間必須在30s內(nèi),否則將被系統(tǒng)聲音替換,同時(shí)自定義聲音文件必須放到main boundle中。
  • 本地通知的數(shù)量是有限制的,最近的本地通知最多只能有64個(gè),超過這個(gè)數(shù)量將被系統(tǒng)忽略。
  • 如果想要移除本地通知可以調(diào)用UIApplication的cancelLocalNotification:或cancelAllLocalNotifications移除指定通知或所有通知。

從上面的程序可以看到userInfo這個(gè)屬性我們?cè)O(shè)置了參數(shù),那么這個(gè)參數(shù)如何接收呢?

在iOS中如果點(diǎn)擊一個(gè)彈出通知(或者鎖屏界面滑動(dòng)查看通知),默認(rèn)會(huì)自動(dòng)打開當(dāng)前應(yīng)用。由于通知由系統(tǒng)調(diào)度那么此時(shí)進(jìn)入應(yīng)用有兩種情況:如果應(yīng)用程序已經(jīng)完全退出那么此時(shí)會(huì)調(diào)用- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法;如果此時(shí)應(yīng)用程序還在運(yùn)行(無論是在前臺(tái)還是在后臺(tái))則會(huì)調(diào)用-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification方法接收消息參數(shù)。當(dāng)然如果是后者自然不必多說,因?yàn)閰?shù)中已經(jīng)可以拿到notification對(duì)象,只要讀取userInfo屬性即可。如果是前者的話則可以訪問launchOptions中鍵為UIApplicationLaunchOptionsLocalNotificationKey的對(duì)象,這個(gè)對(duì)象就是發(fā)送的通知,由此對(duì)象再去訪問userInfo。為了演示這個(gè)過程在下面的程序中將userInfo的內(nèi)容寫入文件以便模擬關(guān)閉程序后再通過點(diǎn)擊通知打開應(yīng)用獲取userInfo的過程。

AppDelegate.m

//
// AppDelegate.m
// LocalNotification
//
// Created by Kenshin Cui on 14/03/28.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "AppDelegate.h"
#import "KCMainViewController.h"

@interface AppDelegate ()

@end

@implementation AppDelegate

#pragma mark - 應(yīng)用代理方法
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  
  _window=[[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
  
  _window.backgroundColor =[UIColor colorWithRed:249/255.0 green:249/255.0 blue:249/255.0 alpha:1];
  
  //設(shè)置全局導(dǎo)航條風(fēng)格和顏色
  [[UINavigationBar appearance] setBarTintColor:[UIColor colorWithRed:23/255.0 green:180/255.0 blue:237/255.0 alpha:1]];
  [[UINavigationBar appearance] setBarStyle:UIBarStyleBlack];
  
  KCMainViewController *mainController=[[KCMainViewController alloc]init];
  _window.rootViewController=mainController;
  
  [_window makeKeyAndVisible];

  //添加通知
  [self addLocalNotification];

  //接收通知參數(shù)
  UILocalNotification *notification=[launchOptions valueForKey:UIApplicationLaunchOptionsLocalNotificationKey];
  NSDictionary *userInfo= notification.userInfo;
  
  [userInfo writeToFile:@"/Users/kenshincui/Desktop/didFinishLaunchingWithOptions.txt" atomically:YES];
  NSLog(@"didFinishLaunchingWithOptions:The userInfo is %@.",userInfo);
  
  return YES;
}

#pragma mark 接收本地通知時(shí)觸發(fā)
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{
  NSDictionary *userInfo=notification.userInfo;
  [userInfo writeToFile:@"/Users/kenshincui/Desktop/didReceiveLocalNotification.txt" atomically:YES];
  NSLog(@"didReceiveLocalNotification:The userInfo is %@",userInfo);
}

#pragma mark 調(diào)用過用戶注冊(cè)通知方法之后執(zhí)行(也就是調(diào)用完registerUserNotificationSettings:方法之后執(zhí)行)
-(void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings{
  if (notificationSettings.types!=UIUserNotificationTypeNone) {
    [self addLocalNotification];
  }
}

#pragma mark 進(jìn)入前臺(tái)后設(shè)置消息信息
-(void)applicationWillEnterForeground:(UIApplication *)application{
  [[UIApplication sharedApplication]setApplicationIconBadgeNumber:0];//進(jìn)入前臺(tái)取消應(yīng)用消息圖標(biāo)
}

#pragma mark - 私有方法
#pragma mark 添加本地通知
-(void)addLocalNotification{
  
  //定義本地通知對(duì)象
  UILocalNotification *notification=[[UILocalNotification alloc]init];
  //設(shè)置調(diào)用時(shí)間
  notification.fireDate=[NSDate dateWithTimeIntervalSinceNow:10.0];//通知觸發(fā)的時(shí)間,10s以后
  notification.repeatInterval=2;//通知重復(fù)次數(shù)
  //notification.repeatCalendar=[NSCalendar currentCalendar];//當(dāng)前日歷,使用前最好設(shè)置時(shí)區(qū)等信息以便能夠自動(dòng)同步時(shí)間
  
  //設(shè)置通知屬性
  notification.alertBody=@"最近添加了諸多有趣的特性,是否立即體驗(yàn)?"; //通知主體
  notification.applicationIconBadgeNumber=1;//應(yīng)用程序圖標(biāo)右上角顯示的消息數(shù)
  notification.alertAction=@"打開應(yīng)用"; //待機(jī)界面的滑動(dòng)動(dòng)作提示
  notification.alertLaunchImage=@"Default";//通過點(diǎn)擊通知打開應(yīng)用時(shí)的啟動(dòng)圖片
  //notification.soundName=UILocalNotificationDefaultSoundName;//收到通知時(shí)播放的聲音,默認(rèn)消息聲音
  notification.soundName=@"msg.caf";//通知聲音(需要真機(jī))
  
  //設(shè)置用戶信息
  notification.userInfo=@{@"id":@1,@"user":@"Kenshin Cui"};//綁定到通知上的其他額外信息
  
  //調(diào)用通知
  [[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
@end

上面的程序可以分為兩種情況去運(yùn)行:一種是啟動(dòng)程序關(guān)閉程序,等到接收到通知之后點(diǎn)擊通知重新進(jìn)入程序;另一種是啟動(dòng)程序后,進(jìn)入后臺(tái)(其實(shí)在前臺(tái)也可以,但是為了明顯的體驗(yàn)這個(gè)過程建議進(jìn)入后臺(tái)),接收到通知后點(diǎn)擊通知進(jìn)入應(yīng)用。另種情況會(huì)分別按照前面說的情況調(diào)用不同的方法接收到userInfo寫入本地文件系統(tǒng)。有了userInfo一般來說就可以根據(jù)這個(gè)信息進(jìn)行一些處理,例如可以根據(jù)不同的參數(shù)信息導(dǎo)航到不同的界面,假設(shè)是更新的通知?jiǎng)t可以導(dǎo)航到更新內(nèi)容界面等。

推送通知

和本地通知不同,推送通知是由應(yīng)用服務(wù)提供商發(fā)起的,通過蘋果的APNs(Apple Push Notification Server)發(fā)送到應(yīng)用客戶端。下面是蘋果官方關(guān)于推送通知的過程示意圖:
推送通知的過程可以分為以下幾步:

  1. 應(yīng)用服務(wù)提供商從服務(wù)器端把要發(fā)送的消息和設(shè)備令牌(device token)發(fā)送給蘋果的消息推送服務(wù)器APNs。
  2. APNs根據(jù)設(shè)備令牌在已注冊(cè)的設(shè)備(iPhone、iPad、iTouch、mac等)查找對(duì)應(yīng)的設(shè)備,將消息發(fā)送給相應(yīng)的設(shè)備。
  3. 客戶端設(shè)備接將接收到的消息傳遞給相應(yīng)的應(yīng)用程序,應(yīng)用程序根據(jù)用戶設(shè)置彈出通知消息。

當(dāng)然,這只是一個(gè)簡(jiǎn)單的流程,有了這個(gè)流程我們還無從下手編寫程序,將上面的流程細(xì)化可以得到如下流程圖(圖片來自互聯(lián)網(wǎng)),在這個(gè)過程中會(huì)也會(huì)提到如何在程序中完成這些步驟:

1.應(yīng)用程序注冊(cè)APNs推送消息。

說明:

a.只有注冊(cè)過的應(yīng)用才有可能接收到消息,程序中通常通過UIApplication的registerUserNotificationSettings:方法注冊(cè),iOS8中通知注冊(cè)的方法發(fā)生了改變,如果是iOS7及之前版本的iOS請(qǐng)參考其他代碼。

b.注冊(cè)之前有兩個(gè)前提條件必須準(zhǔn)備好:開發(fā)配置文件(provisioning profile,也就是.mobileprovision后綴的文件)的App ID不能使用通配ID必須使用指定APP ID并且生成配置文件中選擇Push Notifications服務(wù),一般的開發(fā)配置文件無法完成注冊(cè);應(yīng)用程序的Bundle Identifier必須和生成配置文件使用的APP ID完全一致。

2.iOS從APNs接收device token,在應(yīng)用程序獲取device token。

說明:

a.在UIApplication的-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken代理方法中獲取令牌,此方法發(fā)生在注冊(cè)之后。

b.如果無法正確獲得device token可以在UIApplication的-(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error代理方法中查看詳細(xì)錯(cuò)誤信息,此方法發(fā)生在獲取device token失敗之后。

c.必須真機(jī)調(diào)試,模擬器無法獲取device token。

3.iOS應(yīng)用將device token發(fā)送給應(yīng)用程序提供商,告訴服務(wù)器端當(dāng)前設(shè)備允許接收消息。

說明:

a.device token的生成算法只有Apple掌握,為了確保算法發(fā)生變化后仍然能夠正常接收服務(wù)器端發(fā)送的通知,每次應(yīng)用程序啟動(dòng)都重新獲得device token(注意:device token的獲取不會(huì)造成性能問題,蘋果官方已經(jīng)做過優(yōu)化)。

b.通常可以創(chuàng)建一個(gè)網(wǎng)絡(luò)連接發(fā)送給應(yīng)用程序提供商的服務(wù)器端, 在這個(gè)過程中最好將上一次獲得的device token存儲(chǔ)起來,避免重復(fù)發(fā)送,一旦發(fā)現(xiàn)device token發(fā)生了變化最好將原有的device token一塊發(fā)送給服務(wù)器端,服務(wù)器端刪除原有令牌存儲(chǔ)新令牌避免服務(wù)器端發(fā)送無效消息。

4.應(yīng)用程序提供商在服務(wù)器端根據(jù)前面發(fā)送過來的device token組織信息發(fā)送給APNs。

說明:

a.發(fā)送時(shí)指定device token和消息內(nèi)容,并且完全按照蘋果官方的消息格式組織消息內(nèi)容,通常情況下可以借助其他第三方消息推送框架來完成。

5.APNs根據(jù)消息中的device token查找已注冊(cè)的設(shè)備推送消息。

說明:

a.正常情況下可以根據(jù)device token將消息成功推送到客戶端設(shè)備中,但是也不排除用戶卸載程序的情況,此時(shí)推送消息失敗,APNs會(huì)將這個(gè)錯(cuò)誤消息通知服務(wù)器端以避免資源浪費(fèi)(服務(wù)器端此時(shí)可以根據(jù)錯(cuò)誤刪除已經(jīng)存儲(chǔ)的device token,下次不再發(fā)送)。

下面將簡(jiǎn)單演示一下推送通知的簡(jiǎn)單流程:

首先,看一下iOS客戶端代碼:

//
// AppDelegate.m
// pushnotification
//
// Created by Kenshin Cui on 14/03/27.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "AppDelegate.h"
#import "KCMainViewController.h"

@interface AppDelegate ()

@end

@implementation AppDelegate

#pragma mark - 應(yīng)用程序代理方法
#pragma mark 應(yīng)用程序啟動(dòng)之后
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  
  _window=[[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
  
  _window.backgroundColor =[UIColor colorWithRed:249/255.0 green:249/255.0 blue:249/255.0 alpha:1];
  
  //設(shè)置全局導(dǎo)航條風(fēng)格和顏色
  [[UINavigationBar appearance] setBarTintColor:[UIColor colorWithRed:23/255.0 green:180/255.0 blue:237/255.0 alpha:1]];
  [[UINavigationBar appearance] setBarStyle:UIBarStyleBlack];
  
  KCMainViewController *mainController=[[KCMainViewController alloc]init];
  _window.rootViewController=mainController;
  
  [_window makeKeyAndVisible];
  
  //注冊(cè)推送通知(注意iOS8注冊(cè)方法發(fā)生了變化)
  [application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
  [application registerForRemoteNotifications];
  
  return YES;
}
#pragma mark 注冊(cè)推送通知之后
//在此接收設(shè)備令牌
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
  [self addDeviceToken:deviceToken];
  NSLog(@"device token:%@",deviceToken);
}

#pragma mark 獲取device token失敗后
-(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{
  NSLog(@"didFailToRegisterForRemoteNotificationsWithError:%@",error.localizedDescription);
  [self addDeviceToken:nil];
}

#pragma mark 接收到推送通知之后
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
  NSLog(@"receiveRemoteNotification,userInfo is %@",userInfo);
}

#pragma mark - 私有方法
/**
 * 添加設(shè)備令牌到服務(wù)器端
 *
 * @param deviceToken 設(shè)備令牌
 */
-(void)addDeviceToken:(NSData *)deviceToken{
  NSString *key=@"DeviceToken";
  NSData *oldToken= [[NSUserDefaults standardUserDefaults]objectForKey:key];
  //如果偏好設(shè)置中的已存儲(chǔ)設(shè)備令牌和新獲取的令牌不同則存儲(chǔ)新令牌并且發(fā)送給服務(wù)器端
  if (![oldToken isEqualToData:deviceToken]) {
    [[NSUserDefaults standardUserDefaults] setObject:deviceToken forKey:key];
    [self sendDeviceTokenWidthOldDeviceToken:oldToken newDeviceToken:deviceToken];
  }
}

-(void)sendDeviceTokenWidthOldDeviceToken:(NSData *)oldToken newDeviceToken:(NSData *)newToken{
  //注意一定確保真機(jī)可以正常訪問下面的地址
  NSString *urlStr=@"http://192.168.1.101/RegisterDeviceToken.aspx";
  urlStr=[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
  NSURL *url=[NSURL URLWithString:urlStr];
  NSMutableURLRequest *requestM=[NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:10.0];
  [requestM setHTTPMethod:@"POST"];
  NSString *bodyStr=[NSString stringWithFormat:@"oldToken=%@&newToken=%@",oldToken,newToken];
  NSData *body=[bodyStr dataUsingEncoding:NSUTF8StringEncoding];
  [requestM setHTTPBody:body];
  NSURLSession *session=[NSURLSession sharedSession];
  NSURLSessionDataTask *dataTask= [session dataTaskWithRequest:requestM completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    if (error) {
      NSLog(@"Send failure,error is :%@",error.localizedDescription);
    }else{
      NSLog(@"Send Success!");
    }
    
  }];
  [dataTask resume];
}
@end

iOS客戶端代碼的代碼比較簡(jiǎn)單,注冊(cè)推送通知,獲取device token存儲(chǔ)到偏好設(shè)置中,并且如果新獲取的device token不同于偏好設(shè)置中存儲(chǔ)的數(shù)據(jù)則發(fā)送給服務(wù)器端,更新服務(wù)器端device token列表。

其次,由于device token需要發(fā)送給服務(wù)器端,這里使用一個(gè)Web應(yīng)用作為服務(wù)器端接收device token,這里使用了ASP.NET程序來處理令牌接收注冊(cè)工作,當(dāng)然你使用其他技術(shù)同樣沒有問題。下面是對(duì)應(yīng)的后臺(tái)代碼:

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using CMJ.Framework.Data;

namespace WebServer
{
  public partial class RegisterDeviceToken : System.Web.UI.Page
  {
    private string _appID = @"com.cmjstudio.pushnotification";
    private SqlHelper _helper = new SqlHelper();
    protected void Page_Load(object sender, EventArgs e)
    {
      try
      {
        string oldToken = Request["oldToken"] + "";
        string newToken = Request["newToken"] + "";
        string sql = "";
        //如果傳遞舊的設(shè)備令牌則刪除舊令牌添加新令牌
        if (oldToken != "")
        {
          sql = string.Format("DELETE FROM dbo.Device WHERE AppID='{0}' AND DeviceToken='{1}';", _appID, oldToken);
        }
        sql += string.Format(@"IF NOT EXISTS (SELECT ID FROM dbo.Device WHERE AppID='{0}' AND DeviceToken='{1}')
                    INSERT INTO dbo.Device ( AppID, DeviceToken ) VALUES ( N'{0}', N'{1}');", _appID, newToken);
        _helper.ExecuteNonQuery(sql);
        Response.Write("注冊(cè)成功!");
      }
      catch(Exception ex)
      {
        Response.Write("注冊(cè)失敗,錯(cuò)誤詳情:"+ex.ToString());
      }
    }
  }
}

這個(gè)過程主要就是保存device token到數(shù)據(jù)庫中,當(dāng)然如果同時(shí)傳遞舊的設(shè)備令牌還需要先刪除就的設(shè)備令牌,這里簡(jiǎn)單的在數(shù)據(jù)庫中創(chuàng)建了一張Device表來保存設(shè)備令牌,其中記錄了應(yīng)用程序Id和設(shè)備令牌。

第三步就是服務(wù)器端發(fā)送消息,如果要給APNs發(fā)送消息就必須按照Apple的標(biāo)準(zhǔn)消息格式組織消息內(nèi)容。但是好在目前已經(jīng)有很多開源的第三方類庫供我們使用,具體消息如何包裝完全不用自己組織,這里使用一個(gè)開源的類庫Push Sharp來給APNs發(fā)送消息 ,除了可以給Apple設(shè)備推送消息,Push Sharp還支持Android、Windows Phone等多種設(shè)備,更多詳細(xì)內(nèi)容大家可以參照官方說明。前面說過如果要開發(fā)消息推送應(yīng)用不能使用一般的開發(fā)配置文件,這里還需要注意:如果服務(wù)器端要給APNs發(fā)送消息其秘鑰也必須是通過APNs Development iOS類型的證書來導(dǎo)出的,一般的iOS Development 類型的證書導(dǎo)出的秘鑰無法用作服務(wù)器端發(fā)送秘鑰。下面通過在一個(gè)簡(jiǎn)單的WinForm程序中調(diào)用Push Sharp給APNs發(fā)送消息,這里讀取之前Device表中的所有設(shè)備令牌循環(huán)發(fā)送消息:

using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using PushSharp;
using PushSharp.Apple;
using CMJ.Framework.Data;
using CMJ.Framework.Logging;
using CMJ.Framework.Windows.Forms;

namespace PushNotificationServer
{
  public partial class frmMain : PersonalizeForm
  {
    private string _appID = @"com.cmjstudio.pushnotification";
    private SqlHelper _helper = new SqlHelper();
    public frmMain()
    {
      InitializeComponent();
    }

    private void btnClose_Click(object sender, EventArgs e)
    {
      this.Close();
    }

    private void btnSend_Click(object sender, EventArgs e)
    {
      List<string> deviceTokens = GetDeviceToken();
      SendMessage(deviceTokens, tbMessage.Text);
    }

    #region 發(fā)送消息
    /// <summary>
    /// 取得所有設(shè)備令牌
    /// </summary>
    /// <returns>設(shè)備令牌</returns>
    private List<string> GetDeviceToken()
    {
      List<string> deviceTokens = new List<string>();
      string sql = string.Format("SELECT DeviceToken FROM dbo.Device WHERE AppID='{0}'",_appID);
      DataTable dt = _helper.GetDataTable(sql);
      if(dt.Rows.Count>0)
      {
        foreach(DataRow dr in dt.Rows)
        {
          deviceTokens.Add((dr["DeviceToken"]+"").TrimStart('<').TrimEnd('>').Replace(" ",""));
        }
      }
      return deviceTokens;
    }
    
    /// <summary>
    /// 發(fā)送消息
    /// </summary>
    /// <param name="deviceToken">設(shè)備令牌</param>
    /// <param name="message">消息內(nèi)容</param>
    private void SendMessage(List<string> deviceToken, string message)
    {
      //創(chuàng)建推送對(duì)象
      var pusher = new PushBroker();
      pusher.OnNotificationSent += pusher_OnNotificationSent;//發(fā)送成功事件
      pusher.OnNotificationFailed += pusher_OnNotificationFailed;//發(fā)送失敗事件
      pusher.OnChannelCreated += pusher_OnChannelCreated;
      pusher.OnChannelDestroyed += pusher_OnChannelDestroyed;
      pusher.OnChannelException += pusher_OnChannelException;
      pusher.OnDeviceSubscriptionChanged += pusher_OnDeviceSubscriptionChanged;
      pusher.OnDeviceSubscriptionExpired += pusher_OnDeviceSubscriptionExpired;
      pusher.OnNotificationRequeue += pusher_OnNotificationRequeue;
      pusher.OnServiceException += pusher_OnServiceException;
      //注冊(cè)推送服務(wù)
      byte[] certificateData = File.ReadAllBytes(@"E:\KenshinCui_Push.p12");
      pusher.RegisterAppleService(new ApplePushChannelSettings(certificateData, "123"));
      foreach (string token in deviceToken)
      {
        //給指定設(shè)備發(fā)送消息
        pusher.QueueNotification(new AppleNotification()
          .ForDeviceToken(token)
          .WithAlert(message) 
          .WithBadge(1)
          .WithSound("default"));
      }
    }

    void pusher_OnServiceException(object sender, Exception error)
    {
      Console.WriteLine("消息發(fā)送失敗,錯(cuò)誤詳情:" + error.ToString());
      PersonalizeMessageBox.Show(this, "消息發(fā)送失敗,錯(cuò)誤詳情:" + error.ToString(), "系統(tǒng)提示");
    }

    void pusher_OnNotificationRequeue(object sender, PushSharp.Core.NotificationRequeueEventArgs e)
    {
      Console.WriteLine("pusher_OnNotificationRequeue");
    }

    void pusher_OnDeviceSubscriptionExpired(object sender, string expiredSubscriptionId, DateTime expirationDateUtc, PushSharp.Core.INotification notification)
    {
      Console.WriteLine("pusher_OnDeviceSubscriptionChanged");
    }

    void pusher_OnDeviceSubscriptionChanged(object sender, string oldSubscriptionId, string newSubscriptionId, PushSharp.Core.INotification notification)
    {
      Console.WriteLine("pusher_OnDeviceSubscriptionChanged");
    }

    void pusher_OnChannelException(object sender, PushSharp.Core.IPushChannel pushChannel, Exception error)
    {
      Console.WriteLine("消息發(fā)送失敗,錯(cuò)誤詳情:" + error.ToString());
      PersonalizeMessageBox.Show(this, "消息發(fā)送失敗,錯(cuò)誤詳情:" + error.ToString(), "系統(tǒng)提示");
    }

    void pusher_OnChannelDestroyed(object sender)
    {
      Console.WriteLine("pusher_OnChannelDestroyed");
    }

    void pusher_OnChannelCreated(object sender, PushSharp.Core.IPushChannel pushChannel)
    {
      Console.WriteLine("pusher_OnChannelCreated");
    }

    void pusher_OnNotificationFailed(object sender, PushSharp.Core.INotification notification, Exception error)
    {
      Console.WriteLine("消息發(fā)送失敗,錯(cuò)誤詳情:" + error.ToString());
      PersonalizeMessageBox.Show(this, "消息發(fā)送失敗,錯(cuò)誤詳情:"+error.ToString(), "系統(tǒng)提示");
    }

    void pusher_OnNotificationSent(object sender, PushSharp.Core.INotification notification)
    {
      Console.WriteLine("消息發(fā)送成功!");
      PersonalizeMessageBox.Show(this, "消息發(fā)送成功!", "系統(tǒng)提示");
    }
    #endregion
  }
}

服務(wù)器端消息發(fā)送應(yīng)用運(yùn)行效果:

iOS客戶端接收的消息的效果:

到目前為止通過服務(wù)器端應(yīng)用可以順利發(fā)送消息給APNs并且iOS應(yīng)用已經(jīng)成功接收推送消息。

補(bǔ)充--iOS開發(fā)證書、秘鑰

iOS開發(fā)過程中如果需要進(jìn)行真機(jī)調(diào)試、發(fā)布需要注冊(cè)申請(qǐng)很多證書,對(duì)于初學(xué)者往往迷惑不解,再加上今天的文章中會(huì)牽扯到一些特殊配置,這里就簡(jiǎn)單的對(duì)iOS開發(fā)的常用證書和秘鑰等做一說明。

證書

iOS常用的證書包括開發(fā)證書和發(fā)布證書,無論是真機(jī)調(diào)試還是最終發(fā)布應(yīng)用到App Store這兩個(gè)證書都是必須的,它是iOS開發(fā)的基本證書。

a.開發(fā)證書:開發(fā)證書又分為普通開發(fā)證書和推送證書,如果僅僅是一般的應(yīng)用則前者即可滿足,但是如果開發(fā)推送應(yīng)用則必須使用推送證書。

b.發(fā)布證書:發(fā)布證書又可以分為普通發(fā)布證書、推送證書、Pass Type ID證書、站點(diǎn)發(fā)布證書、VoIP服務(wù)證書、蘋果支付證書。同樣的,對(duì)于需要使用特殊服務(wù)的應(yīng)用則必須選擇對(duì)應(yīng)的證書。

應(yīng)用標(biāo)識(shí)

App ID,應(yīng)用程序的唯一標(biāo)識(shí),對(duì)應(yīng)iOS應(yīng)用的Bundle Identifier,App ID在蘋果開發(fā)者中心中分為通配應(yīng)用ID和明確的應(yīng)用ID,前者一般用于普通應(yīng)用開發(fā),一個(gè)ID可以適用于多個(gè)不同標(biāo)識(shí)的應(yīng)用;但是對(duì)于使用消息推送、Passbook、站點(diǎn)發(fā)布、iCloud等服務(wù)的應(yīng)用必須配置明確的應(yīng)用ID。

設(shè)備標(biāo)識(shí)

UDID,用于標(biāo)識(shí)每一臺(tái)硬件設(shè)備的標(biāo)示符。注意它不是device token,device token是根據(jù)UDID使用一個(gè)只有Apple自己才知道的算法生成的一組標(biāo)示符。

配置簡(jiǎn)介

Provisioning Profiles,平時(shí)又稱為PP文件。將UDID、App ID、開發(fā)證書打包在一起的配置文件,同樣分為開發(fā)和發(fā)布兩類配置文件。

秘鑰

在申請(qǐng)開發(fā)證書時(shí)必須要首先提交一個(gè)秘鑰請(qǐng)求文件,對(duì)于生成秘鑰請(qǐng)求文件的mac,如果要做開發(fā)則只需要下載證書和配置簡(jiǎn)介即可開發(fā)。但是如果要想在其他機(jī)器上做開發(fā)則必須將證書中的秘鑰導(dǎo)出(導(dǎo)出之后是一個(gè).p12文件),然后導(dǎo)入其他機(jī)器。同時(shí)對(duì)于類似于推送服務(wù)器端應(yīng)用如果要給APNs發(fā)送消息,同樣需要使用.p12秘鑰文件,并且這個(gè)秘鑰文件需要是推送證書導(dǎo)出的對(duì)應(yīng)秘鑰。

補(bǔ)充--通知中心

對(duì)于很多初學(xué)者往往會(huì)把iOS中的本地通知、推送通知和iOS通知中心的概念弄混。其實(shí)二者之間并沒有任何關(guān)系,事實(shí)上它們都不屬于一個(gè)框架,前者屬于UIKit框架,后者屬于Foundation框架。

通知中心實(shí)際上是iOS程序內(nèi)部之間的一種消息廣播機(jī)制,主要為了解決應(yīng)用程序內(nèi)部不同對(duì)象之間解耦而設(shè)計(jì)。它是基于觀察者模式設(shè)計(jì)的,不能跨應(yīng)用程序進(jìn)程通信,當(dāng)通知中心接收到消息之后會(huì)根據(jù)內(nèi)部的消息轉(zhuǎn)發(fā)表,將消息發(fā)送給訂閱者。下面是一個(gè)簡(jiǎn)單的流程示意圖:
了解通知中心需要熟悉NSNotificationCenter和NSNotification兩個(gè)類:

NSNotificationCenter:是通知系統(tǒng)的中心,用于注冊(cè)和發(fā)送通知,下表列出常用的方法。

方法 說明
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject 添加監(jiān)聽,參數(shù):
observer:監(jiān)聽者
selector:監(jiān)聽方法(監(jiān)聽者監(jiān)聽到通知后執(zhí)行的方法)
  name:監(jiān)聽的通知名稱
object:通知的發(fā)送者(如果指定nil則監(jiān)聽任何對(duì)象發(fā)送的通知)
- (id <NSObject>)addObserverForName:(NSString *)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block 添加監(jiān)聽,參數(shù):
name:監(jiān)聽的通知名稱
object:通知的發(fā)送者(如果指定nil則監(jiān)聽任何對(duì)象發(fā)送的通知)
queue:操作隊(duì)列,如果制定非主隊(duì)線程隊(duì)列則可以異步執(zhí)行block
block:監(jiān)聽到通知后執(zhí)行的操作
- (void)postNotification:(NSNotification *)notification 發(fā)送通知,參數(shù):
notification:通知對(duì)象
- (void)postNotificationName:(NSString *)aName object:(id)anObject 發(fā)送通知,參數(shù):
aName:通知名稱
anObject:通知發(fā)送者
- (void)postNotificationName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo 發(fā)送通知,參數(shù):
aName:通知名稱
anObject:通知發(fā)送者
aUserInfo:通知參數(shù)
- (void)removeObserver:(id)observer 移除監(jiān)聽,參數(shù):
observer:監(jiān)聽對(duì)象
- (void)removeObserver:(id)observer name:(NSString *)aName object:(id)anObject 移除監(jiān)聽,參數(shù):
observer:監(jiān)聽對(duì)象
aName:通知名稱
anObject:通知發(fā)送者

NSNotification:代表通知內(nèi)容的載體,主要有三個(gè)屬性:name代表通知名稱,object代表通知的發(fā)送者,userInfo代表通知的附加信息。

雖然前面的文章中從未提到過通知中心,但是其實(shí)通知中心我們并不陌生,前面文章中很多內(nèi)容都是通過通知中心來進(jìn)行應(yīng)用中各個(gè)組件通信的,只是沒有單獨(dú)拿出來說而已。例如前面的文章中討論的應(yīng)用程序生命周期問題,當(dāng)應(yīng)用程序啟動(dòng)后、進(jìn)入后臺(tái)、進(jìn)入前臺(tái)、獲得焦點(diǎn)、失去焦點(diǎn),窗口大小改變、隱藏等都會(huì)發(fā)送通知。這個(gè)通知可以通過前面NSNotificationCenter進(jìn)行訂閱即可接收對(duì)應(yīng)的消息,下面的示例演示了如何添加監(jiān)聽獲得UIApplication的進(jìn)入后臺(tái)和獲得焦點(diǎn)的通知:

//
// KCMainViewController.m
// NotificationCenter
//
// Created by Kenshin Cui on 14/03/27.
// Copyright (c) 2014年 cmjstudio. All rights reserved.
//

#import "KCMainViewController.h"

@interface KCMainViewController ()

@end

@implementation KCMainViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  
  [self addObserverToNotificationCenter];
  
}

#pragma mark 添加監(jiān)聽
-(void)addObserverToNotificationCenter{
  /*添加應(yīng)用程序進(jìn)入后臺(tái)監(jiān)聽
   * observer:監(jiān)聽者
   * selector:監(jiān)聽方法(監(jiān)聽者監(jiān)聽到通知后執(zhí)行的方法)
   * name:監(jiān)聽的通知名稱(下面的UIApplicationDidEnterBackgroundNotification是一個(gè)常量)
   * object:通知的發(fā)送者(如果指定nil則監(jiān)聽任何對(duì)象發(fā)送的通知)
   */
  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:[UIApplication sharedApplication]];
  
  /* 添加應(yīng)用程序獲得焦點(diǎn)的通知監(jiān)聽
   * name:監(jiān)聽的通知名稱
   * object:通知的發(fā)送者(如果指定nil則監(jiān)聽任何對(duì)象發(fā)送的通知)
   * queue:操作隊(duì)列,如果制定非主隊(duì)線程隊(duì)列則可以異步執(zhí)行block
   * block:監(jiān)聽到通知后執(zhí)行的操作
   */
  NSOperationQueue *operationQueue=[[NSOperationQueue alloc]init];
  [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification object:[UIApplication sharedApplication] queue:operationQueue usingBlock:^(NSNotification *note) {
    NSLog(@"Application become active.");
  }];
}

#pragma mark 應(yīng)用程序啟動(dòng)監(jiān)聽方法
-(void)applicationEnterBackground{
  NSLog(@"Application enter background.");
}
@end

當(dāng)然很多時(shí)候使用通知中心是為了添加自定義通知,并獲得自定義通知消息。在前面的文章“iOS開發(fā)系列--視圖切換”中提到過如何進(jìn)行多視圖之間參數(shù)傳遞,其實(shí)利用自定義通知也可以進(jìn)行參數(shù)傳遞。通常一個(gè)應(yīng)用登錄后會(huì)顯示用戶信息,而登錄信息可以通過登錄界面獲取。下面就以這樣一種場(chǎng)景為例,在主界面中添加監(jiān)聽,在登錄界面發(fā)送通知,一旦登錄成功將向通知中心發(fā)送成功登錄的通知,此時(shí)主界面中由于已經(jīng)添加通知監(jiān)聽所以會(huì)收到通知并更新UI界面。

主界面KCMainViewController.m:

//
// KCMainViewController.m
// NotificationCenter
//
// Created by Kenshin Cui on 14/03/27
// Copyright (c) 2014年 cmjstudio. All rights reserved.
//

#import "KCMainViewController.h"
#import "KCLoginViewController.h"
#define UPDATE_LGOGIN_INFO_NOTIFICATION @"updateLoginInfo"

@interface KCMainViewController (){
  UILabel *_lbLoginInfo;
  UIButton *_btnLogin;
}

@end

@implementation KCMainViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  
  [self setupUI];
}

-(void)setupUI{
  UILabel *label =[[UILabel alloc]initWithFrame:CGRectMake(0, 100,320 ,30)];
  label.textAlignment=NSTextAlignmentCenter;
  label.textColor=[UIColor colorWithRed:23/255.0 green:180/255.0 blue:237/255.0 alpha:1];
  _lbLoginInfo=label;
  [self.view addSubview:label];
  
  UIButton *button=[UIButton buttonWithType:UIButtonTypeSystem];
  button.frame=CGRectMake(60, 200, 200, 25);
  [button setTitle:@"登錄" forState:UIControlStateNormal];
  [button addTarget:self action:@selector(loginOut) forControlEvents:UIControlEventTouchUpInside];
  _btnLogin=button;
  
  [self.view addSubview:button];
}

-(void)loginOut{
  //添加監(jiān)聽
  [self addObserverToNotification];
  
  KCLoginViewController *loginController=[[KCLoginViewController alloc]init];
  
  [self presentViewController:loginController animated:YES completion:nil];
}

/**
 * 添加監(jiān)聽
 */
-(void)addObserverToNotification{
  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateLoginInfo:) name:UPDATE_LGOGIN_INFO_NOTIFICATION object:nil];
}

/**
 * 更新登錄信息,注意在這里可以獲得通知對(duì)象并且讀取附加信息
 */
-(void)updateLoginInfo:(NSNotification *)notification{
  NSDictionary *userInfo=notification.userInfo;
  _lbLoginInfo.text=userInfo[@"loginInfo"];
  _btnLogin.titleLabel.text=@"注銷";
}

-(void)dealloc{
  //移除監(jiān)聽
  [[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end
登錄界面KCLoginViewController.m:

//
// KCLoginViewController.m
// NotificationCenter
//
// Created by Kenshin Cui on 14/03/27.
// Copyright (c) 2014年 cmjstudio. All rights reserved.
//

#import "KCLoginViewController.h"
#define UPDATE_LGOGIN_INFO_NOTIFICATION @"updateLoginInfo"

@interface KCLoginViewController (){
  UITextField *_txtUserName;
  UITextField *_txtPassword;
}

@end

@implementation KCLoginViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  
  [self setupUI];
}

/**
 * UI布局
 */
-(void)setupUI{
  //用戶名
  UILabel *lbUserName=[[UILabel alloc]initWithFrame:CGRectMake(50, 150, 100, 30)];
  lbUserName.text=@"用戶名:";
  [self.view addSubview:lbUserName];
  
  _txtUserName=[[UITextField alloc]initWithFrame:CGRectMake(120, 150, 150, 30)];
  _txtUserName.borderStyle=UITextBorderStyleRoundedRect;
  [self.view addSubview:_txtUserName];
  
  //密碼
  UILabel *lbPassword=[[UILabel alloc]initWithFrame:CGRectMake(50, 200, 100, 30)];
  lbPassword.text=@"密碼:";
  [self.view addSubview:lbPassword];
  
  _txtPassword=[[UITextField alloc]initWithFrame:CGRectMake(120, 200, 150, 30)];
  _txtPassword.secureTextEntry=YES;
  _txtPassword.borderStyle=UITextBorderStyleRoundedRect;
  [self.view addSubview:_txtPassword];
  
  //登錄按鈕
  UIButton *btnLogin=[UIButton buttonWithType:UIButtonTypeSystem];
  btnLogin.frame=CGRectMake(70, 270, 80, 30);
  [btnLogin setTitle:@"登錄" forState:UIControlStateNormal];
  [self.view addSubview:btnLogin];
  [btnLogin addTarget:self action:@selector(login) forControlEvents:UIControlEventTouchUpInside];
  
  //取消登錄按鈕
  UIButton *btnCancel=[UIButton buttonWithType:UIButtonTypeSystem];
  btnCancel.frame=CGRectMake(170, 270, 80, 30);
  [btnCancel setTitle:@"取消" forState:UIControlStateNormal];
  [self.view addSubview:btnCancel];
  [btnCancel addTarget:self action:@selector(cancel) forControlEvents:UIControlEventTouchUpInside];
}

#pragma mark 登錄操作
-(void)login{
  if ([_txtUserName.text isEqualToString:@"kenshincui"] && [_txtPassword.text isEqualToString:@"123"] ) {
    //發(fā)送通知
    [self postNotification];
    [self dismissViewControllerAnimated:YES completion:nil];
  }else{
    //登錄失敗彈出提示信息
    UIAlertView *alertView=[[UIAlertView alloc]initWithTitle:@"系統(tǒng)信息" message:@"用戶名或密碼錯(cuò)誤,請(qǐng)重新輸入!" delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:nil];
    [alertView show];
  }
  
}

#pragma mark 點(diǎn)擊取消
-(void)cancel{
  [self dismissViewControllerAnimated:YES completion:nil];
}

/**
 * 添加通知,注意這里設(shè)置了附加信息
 */
-(void)postNotification{
  NSDictionary *userInfo=@{@"loginInfo":[NSString stringWithFormat:@"Hello,%@!",_txtUserName.text]};
  NSLog(@"%@",userInfo);
  NSNotification *notification=[NSNotification notificationWithName:UPDATE_LGOGIN_INFO_NOTIFICATION object:self userInfo:userInfo];
  [[NSNotificationCenter defaultCenter] postNotification:notification];
//也可直接采用下面的方法
//  [[NSNotificationCenter defaultCenter] postNotificationName:UPDATE_LGOGIN_INFO_NOTIFICATION object:self userInfo:userInfo];

}
@end

運(yùn)行效果:

注意:

通過上面的介紹大家應(yīng)該可以發(fā)現(xiàn)其實(shí)通知中心是一種低耦合設(shè)計(jì),和前面文章中提到的代理模式有異曲同工之妙。相對(duì)于后者而言,通知中心可以將一個(gè)通知發(fā)送給多個(gè)監(jiān)聽者,而每個(gè)對(duì)象的代理卻只能有一個(gè)。當(dāng)然代理也有其優(yōu)點(diǎn),例如使用代理代碼分布結(jié)構(gòu)更加清晰,它不像通知一樣隨處都可以添加訂閱等,實(shí)際使用過程中需要根據(jù)實(shí)際情況而定。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • iOS用AutoLayout實(shí)現(xiàn)分頁滾動(dòng)功能

    iOS用AutoLayout實(shí)現(xiàn)分頁滾動(dòng)功能

    這篇文章主要給大家介紹了關(guān)于iOS用AutoLayout實(shí)現(xiàn)分頁滾動(dòng)功能的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)各位iOS開發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • iOS 獲得現(xiàn)在的時(shí)間代碼

    iOS 獲得現(xiàn)在的時(shí)間代碼

    本文通過實(shí)例代碼給大家介紹了iOS 獲得現(xiàn)在的時(shí)間的方法,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有參考借鑒價(jià)值,需要的的朋友參考下吧
    2017-06-06
  • iOS利用NSAttributedString實(shí)現(xiàn)圖文混排效果示例

    iOS利用NSAttributedString實(shí)現(xiàn)圖文混排效果示例

    iOS7以后,因?yàn)門extKit的強(qiáng)大,可以用NSAttributedString很方便的實(shí)現(xiàn)圖文混排(主要是利用了NSTextAttachment),所以下面這篇文章主要給大家介紹了關(guān)于iOS利用NSAttributedString實(shí)現(xiàn)圖文混排效果的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-10-10
  • iOS實(shí)現(xiàn)點(diǎn)擊微信頭像(放大、縮放、保存)效果

    iOS實(shí)現(xiàn)點(diǎn)擊微信頭像(放大、縮放、保存)效果

    最近公司產(chǎn)品需要實(shí)現(xiàn)點(diǎn)擊個(gè)人主頁頭像可以放大頭像、縮放頭像、保存頭像效果(和點(diǎn)擊微信個(gè)人頭像類似),故找個(gè)時(shí)間實(shí)現(xiàn)一下,記錄下來,供自己查看也給有需要的大家做個(gè)參考。下面來一起看看吧。
    2017-03-03
  • iOS添加購物車動(dòng)畫效果示例

    iOS添加購物車動(dòng)畫效果示例

    本篇文章主要介紹了iOS 購物車動(dòng)畫效果示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-02-02
  • 解析iOS10中的極光推送消息的適配

    解析iOS10中的極光推送消息的適配

    這篇文章主要介紹了解析iOS10中的極光推送消息的適配的相關(guān)資料,我們需要先安裝Xcode8.0版本,接下來本文分步驟詳細(xì)給大家介紹,需要的朋友可以參考下
    2016-09-09
  • iOS11 SectionHeader 胡亂移動(dòng)且滑動(dòng)時(shí)出現(xiàn)重復(fù)內(nèi)容的解決方法

    iOS11 SectionHeader 胡亂移動(dòng)且滑動(dòng)時(shí)出現(xiàn)重復(fù)內(nèi)容的解決方法

    這篇文章主要介紹了iOS11 SectionHeader 胡亂移動(dòng)且滑動(dòng)時(shí)出現(xiàn)重復(fù)內(nèi)容的解決方法,需要的朋友可以參考下
    2017-11-11
  • iOS判斷是否越獄設(shè)備方法示例

    iOS判斷是否越獄設(shè)備方法示例

    這篇文章主要給大家介紹了關(guān)于iOS判斷是否越獄設(shè)備的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)各位iOS開發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-08-08
  • 詳解使用Xcode7的Instruments檢測(cè)解決iOS內(nèi)存泄露(最新)

    詳解使用Xcode7的Instruments檢測(cè)解決iOS內(nèi)存泄露(最新)

    本篇文章主要介紹使用Xcode7的Instruments檢測(cè)解決iOS內(nèi)存泄露(最新)的相關(guān)資料,需要的朋友可以參考下
    2017-09-09
  • iOS關(guān)鍵字static extern const使用示例詳解

    iOS關(guān)鍵字static extern const使用示例詳解

    這篇文章主要為大家介紹了iOS關(guān)鍵字static extern const使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11

最新評(píng)論