iOS10 语音播报填坑详解(解决串行播报中断问题)
在来聊这类需求的解决方案之前,咱们还是先来聊一聊这类需求的真实使用场景:语音播报。语音播报需求运用最为广泛的应该是收银对账了,就类似于支付宝、微信、收钱吧等的收款语音提示一样。在iOS 10 之前,苹果没有提供通知扩展类的时候,如果想要实现杀进程也可以正常播报语音消息很难,从ios 10添加了这一个通知扩展类后,实现杀进程播报语音就相对简单很多了。
我们先来看一个陌生的Tagget
- Notification Service Extension
这个Notification Service Extension 就是苹果在 iOS 10的新系统中为我们添加的新特性,这个新特性就能帮助我们用来解决杀死进程正常语音播报
苹果官方解释:UNNotificationServiceExtension
详细步骤
- 创建一个通知扩展类
- 添加语音播报逻辑代码
- 设置支持后台播放
- iOS10 一下实现串行播报
创建一个通知扩展类
- 首先我点击 Xcode 的 File -> New -> Target -> Notification Service Extension,新建一个通知扩展类Target。
新建完后,我们的工程会多出一个文件夹,这里示例Demo的Target命名为 NotificationSE,文件夹中有NotificationService.h NotificationService.m 文件,这两个文件就是后面我们要用到的通知扩展类文件
在没有对NotificationService做任何修改时,我们先来预览下 .m 文件中都有哪些内容
从上面的截图,我们可以看到,.m 文件其实很简单,就 2 个函数,其实后面我们对这个文件做逻辑处理,也是很简单的。
添加语音播报逻辑代码
- 注意,这里我们使用的语音合成和播报组件也是苹果官方提供的组件,
AVSpeechSynthesizer
,AVSpeechSynthesisVoice
,AVSpeechUtterance
我们先来看下一段语音播放代码片段:
AVSpeechSynthesizer *av = [[AVSpeechSynthesizer alloc] init]; AVSpeechSynthesisVoice *voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"zh-CN"]; AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc] initWithString:@"我是测试文案"]; utterance.rate = 0.5; utterance.voice= voice; [av speakUtterance:utterance];
现在我们将 NotificationService .m 文件做修改,使之支持语音播报。并且能支持多条通知同时过来的串行播报。完整文件如下:
// // NotificationService.m // NotificationSE // // Created by 刘光强 on 2018/9/17. // Copyright © 2018年 quangqiang. All rights reserved. // #import "NotificationService.h" #import <MediaPlayer/MediaPlayer.h> #import <AVFoundation/AVFoundation.h> @interface NotificationService ()<AVSpeechSynthesizerDelegate> @property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver); @property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent; @property (nonatomic, strong) AVSpeechSynthesisVoice *synthesisVoice; @property (nonatomic, strong) AVSpeechSynthesizer *synthesizer; @end @implementation NotificationService - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { self.contentHandler = contentHandler; self.bestAttemptContent = [request.content mutableCopy]; // 这个info 内容就是通知信息携带的数据,后面我们取语音播报的文案,通知栏的title,以及通知内容都是从这个info字段中获取 NSDictionary *info = self.bestAttemptContent.userInfo; // 播报语音 [self playVoiceWithContent: info[@"content"]]; // 这行代码需要注释,当我们想解决当同时推送了多条消息,这时我们想多条消息一条一条的挨个播报,我们就需要将此行代码注释 // self.contentHandler(self.bestAttemptContent); } - (void)playVoiceWithContent:(NSString *)content { AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:content]; utterance.rate = 0.5; utterance.voice = self.synthesisVoice; [self.synthesizer speakUtterance:utterance]; } // 新增语音播放代理函数,在语音播报完成的代理函数中,我们添加下面的一行代码 - (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance *)utterance { self.contentHandler(self.bestAttemptContent); } - (void)serviceExtensionTimeWillExpire { // Called just before the extension will be terminated by the system. // Use this as an opportunity to d