因?yàn)榈玫揭粋€(gè)新功能:在前臺,后臺微酬,和鎖屏狀態(tài)下,得到后臺通知時(shí)語音播報(bào),查閱一些資料并作出總結(jié)离钝。為了方便理解以下內(nèi)容分為三個(gè)部分:
- APP生命周期
- 合成語音
- 各個(gè)系統(tǒng)下收到通知的邏輯處理
首先我們要先了解APP在各情況下的活躍狀態(tài)以及涉及的系統(tǒng)方法:
1.首次啟動正常前臺運(yùn)行:活躍狀態(tài)
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
return YES;
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
NSLog(@" 程序激活 !");
}
2.鎖屏狀態(tài):掛起->進(jìn)入后臺
- (void)applicationWillResignActive:(UIApplication *)application {
// 比如:當(dāng)有電話進(jìn)來或者鎖屏,這時(shí)你的應(yīng)用程會掛起褪储,在這時(shí)卵渴,UIApplicationDelegate委托會收到通知,調(diào)用 applicationWillResignActive 方法鲤竹,你可以重寫這個(gè)方法浪读,做掛起前的工作,比如關(guān)閉網(wǎng)絡(luò)辛藻,保存數(shù)據(jù)碘橘。
NSLog(@" 程序掛起 !");
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(@" 程序進(jìn)入后臺 !");
}
3.點(diǎn)擊home:掛起->進(jìn)入后臺
- (void)applicationWillResignActive:(UIApplication *)application {
NSLog(@" 程序掛起 !");
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(@" 程序進(jìn)入后臺 !");
}
4.后臺狀態(tài)時(shí),點(diǎn)擊收到的通知或者icon
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
NSLog(@"程序進(jìn)入前臺 !");
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
NSLog(@"程序激活 !");
}
二吱肌、語音部分
用系統(tǒng)強(qiáng)大的AVSpeechSynthesizer(10_14, 7_0)痘拆,寫成一個(gè)單例
+(instancetype)sharedInstance{
static CRSpeechManger *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[CRSpeechManger alloc] init];
instance.synthesizer = [[AVSpeechSynthesizer alloc] init];
});
return instance;
}
- (void)syntheticVoice:(NSString*)string {
// 語音合成
AVSpeechUtterance *speechUtterance = [AVSpeechUtterance speechUtteranceWithString:string];
//設(shè)置語言類別(不能被識別,返回值為nil)
speechUtterance.voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"zh-CN"];
//設(shè)置語速快慢
speechUtterance.rate=0.5;//0.5是一個(gè) //語音合成器會生成音頻
[self.synthesizer speakUtterance:speechUtterance];
}
三氮墨、通知部分
我們先聊本地通知纺蛆,其中iOS10是一個(gè)分水嶺,iOS10以前的系統(tǒng)用UILocalNotification规揪,iOS10以后的系統(tǒng)包括10桥氏,系統(tǒng)新增<UserNotifications/UserNotifications.h>庫,增加自定義多媒體消息的功能猛铅。
1.發(fā)送通知
- (void)pushNotification_IOS10_before{
UILocalNotification *notif = [[UILocalNotification alloc] init];
// 發(fā)出推送的日期
notif.fireDate = [NSDate dateWithTimeIntervalSinceNow:10];
// 推送的內(nèi)容
notif.alertBody = @"本地推送語音測試";
// 可以添加特定信息
notif.userInfo = @{ @"name": @"123" };
// 角標(biāo)
notif.applicationIconBadgeNumber = 1;
// 提示音
notif.soundName = UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] scheduleLocalNotification:notif];
}
-(void)pushNotification_IOS_10_after {
//獲取通知中心用來激活新建的通知
UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];
UNMutableNotificationContent * content = [[UNMutableNotificationContent alloc]init];
content.body = body;
content.userInfo = @{@"name":@"123"};
//通知的提示音
if ([promptTone containsString:@"."]) {
UNNotificationSound *sound = [UNNotificationSound soundNamed:promptTone];
content.sound = sound;
}
__block UNNotificationAttachment *imageAtt;
__block UNNotificationAttachment *movieAtt;
__block UNNotificationAttachment *soundAtt;
NSString * imageName = @"";
if ([imageName containsString:@"."]) {
[self addNotificationAttachmentContent:content attachmentName:imageName options:nil withCompletion:^(NSError *error, UNNotificationAttachment *notificationAtt) {
imageAtt = [notificationAtt copy];
}];
}
NSString * soundName = @"";
if ([soundName containsString:@"."]) {
[self addNotificationAttachmentContent:content attachmentName:soundName options:nil withCompletion:^(NSError *error, UNNotificationAttachment *notificationAtt) {
soundAtt = [notificationAtt copy];
}];
}
NSString * movieName = @"";
if ([movieName containsString:@"."]) {
UNNotificationAttachmentOptionsThumbnailTimeKey
[self addNotificationAttachmentContent:content attachmentName:movieName options:@{@"UNNotificationAttachmentOptionsThumbnailTimeKey":@10} withCompletion:^(NSError *error, UNNotificationAttachment *notificationAtt) {
movieAtt = [notificationAtt copy];
}];
}
NSMutableArray * array = [NSMutableArray array];
// [array addObject:soundAtt];
// [array addObject:imageAtt];
// [array addObject:movieAtt];
content.attachments = array;
//添加通知下拉動作按鈕
NSMutableArray * actionMutableArray = [NSMutableArray array];
UNNotificationAction * actionA = [UNNotificationAction actionWithIdentifier:@"identifierNeedUnlock" title:@"進(jìn)入應(yīng)用" options:UNNotificationActionOptionAuthenticationRequired];
UNNotificationAction * actionB = [UNNotificationAction actionWithIdentifier:@"identifierRed" title:@"忽略" options:UNNotificationActionOptionDestructive];
[actionMutableArray addObjectsFromArray:@[actionA,actionB]];
if (actionMutableArray.count > 1) {
UNNotificationCategory * category = [UNNotificationCategory categoryWithIdentifier:@"categoryNoOperationAction" actions:actionMutableArray intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction];
[center setNotificationCategories:[NSSet setWithObjects:category, nil]];
content.categoryIdentifier = @"categoryNoOperationAction";
}
//UNTimeIntervalNotificationTrigger 延時(shí)推送
//UNCalendarNotificationTrigger 定時(shí)推送
//UNLocationNotificationTrigger 位置變化推送
UNTimeIntervalNotificationTrigger * tirgger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:10 repeats:NO];
//建立通知請求
UNNotificationRequest * request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:tirgger];
//將建立的通知請求添加到通知中心
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (error) {
NSLog(@"%@本地推送 :( 報(bào)錯(cuò) %@",identifier,error);
} else {
NSLog(@"本地推送 :成功");
}
}];
}
2.注冊本地通知
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
if ([[UIDevice currentDevice].systemVersion doubleValue]>=10.0) {
[[UNUserNotificationCenter currentNotificationCenter] setDelegate:self];
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center requestAuthorizationWithOptions:UNAuthorizationOptionAlert|UNAuthorizationOptionBadge|UNAuthorizationOptionSound completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted) {
NSLog(@"新版通知-注冊成功-success");
}else{
NSLog(@"新版通知-注冊失敗-error");
}
}];
}else if([[UIDevice currentDevice].systemVersion doubleValue]>=8.0){//8.0以后使用這種方法來注冊推送通知
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
}else{
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
}
return YES;
}
3.收到本地通知后的處理邏輯
- (void)application:(UIApplication *)application didReceiveLocalNotification:(nonnull UILocalNotification *)notification{
NSString *voiceString =nil;
voiceString = [NSString stringWithFormat:@"語音播報(bào):%@", notification.alertBody];
[[CRSpeechManger sharedInstance] syntheticVoice:voiceString];
}
總結(jié):本地推送較為簡單字支,但要注意的是只有在前臺時(shí)會調(diào)用didReceiveLocalNotification,后臺狀態(tài)時(shí)奕坟,只有用戶點(diǎn)擊后才能調(diào)用此方法祥款。
遠(yuǎn)程通知
1.注冊通知(和本地通知一樣),用戶同意后月杉,會調(diào)用此程序刃跛,獲取系統(tǒng)的deviceToken,應(yīng)把deviceToken傳給服務(wù)器保存苛萎,此函數(shù)會在程序每次啟動時(shí)調(diào)用:
//registerForRemoteNotifications方法調(diào)用時(shí)機(jī)
//對于任何遠(yuǎn)程推送桨昙,registerForRemoteNotifications可以直接調(diào)用來注冊遠(yuǎn)程推送检号,而不需要用戶允許。也就是說只要調(diào)用-[UIApplication registerForRemoteNotifications]蛙酪,就可以在AppDelegate的application:didRegisterForRemoteNotificationsWithDeviceToken:中獲取到設(shè)備的push token齐苛。
那么通常的彈窗詢問權(quán)限有什么用呢?其實(shí)只是請求用戶允許在推送通知到來時(shí)能夠有alert, badge和sound桂塞,而并不是在請求注冊推送本身的權(quán)限凹蜂。
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
}
2.收到消息的邏輯處理
ios10以后,需要實(shí)現(xiàn)[UNUserNotificationCenter currentNotificationCenter]的代理方法阁危。
//用戶點(diǎn)擊通知欄玛痊,前后臺處理方式一致,iOS 10以后是用戶點(diǎn)擊才回調(diào)
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler{
//可按需求進(jìn)行數(shù)據(jù)處理
NSLog(@"%@",response);
}
// 下面處理iOS 10之后狂打,設(shè)置前臺收到遠(yuǎn)程消息時(shí)是否顯示 當(dāng)APP處于前臺的時(shí)候接收到通知
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
{
NSString *voiceString = [NSString stringWithFormat:@"語音播報(bào):%@", notification.request.content.body];
[[CRSpeechManger sharedInstance] syntheticVoice:voiceString];
}
iOS10以前通知回調(diào)方法,前臺收到消息時(shí)調(diào)用擂煞,
后臺模式下
普通推送:收到推送后(有文字有聲音),點(diǎn)開通知趴乡,進(jìn)入APP后对省,才執(zhí)行
靜默推送:收到推送(沒有文字沒有聲音),不用點(diǎn)開通知晾捏,不用打開APP蒿涎,就能執(zhí)行,用戶完全感覺不到粟瞬,可以理解允許應(yīng)用在收到通知后在后臺運(yùn)行一段代碼同仆,且能夠馬上執(zhí)行
靜默推送簡介:靜默推送(Silent Push)并不是必須要“靜默”,只要推送payload中aps字典里包含了"content-available": 1的鍵值對裙品,都具有靜默推送的特性(比如喚醒應(yīng)用)俗批,而無論你是否推了alert, badge或sound。則可以不打擾用戶的情況下進(jìn)行內(nèi)容更新等操作市怎。
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
// 獲取通知所帶的數(shù)據(jù)
//程序關(guān)閉狀態(tài)點(diǎn)擊推送消息打開
NSLog(@"%@",userInfo[@"alert"]);
//前臺運(yùn)行
if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive) {
}
//后臺掛起時(shí)
else{
}
//設(shè)置應(yīng)用程序角標(biāo)數(shù)為0
[UIApplication sharedApplication].applicationIconBadgeNumber = 0;
NSString *voiceString =nil;
voiceString = [NSString stringWithFormat:@"語音播報(bào):%@", userInfo[@"alert"]];
[[CRSpeechManger sharedInstance] syntheticVoice:voiceString];
}
知識點(diǎn)梳理告一段落岁忘,下面討論需求實(shí)現(xiàn)
我的想法是:
iOS 10之前的版本,采用靜默推送区匠,在實(shí)現(xiàn)播放固定的聲音干像。
iOS 10之后,使用新增的UNNotificationServiceExtension
(mutable-content : 1)驰弄,合成語音播報(bào)麻汰。