iOS 10 來點不一樣的推送 (2) - 語音提示

劉小蠻發表於2017-06-13

轉自我的 Blog: Danny's Dream

接著上篇文章,在一個交流群裡有個小夥伴問,怎麼實現支付寶類似收到錢之後的語音播放效果。

結合著之前對推送的研究,想到了兩種實現方案:

  • 1.在 notification 的 extension 中將收到的內容播放出來。
  • 2.將文字轉換成語音檔案,儲存在本地,然後替換為播放的提示音。

直接播放

AVFoundation

其實蘋果有提供原生的文字轉語音的功能,在 AVFoundation 框架中。簡單的使用方法如下:

self.speechSynthesizer = [[AVSpeechSynthesizer alloc] init];
AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:@“收到人民幣1000000"];

AVSpeechSynthesisVoice *voiceType = [AVSpeechSynthesisVoice voiceWithLanguage:@"en-US"];
utterance.voice = voiceType;
//設定語速
utterance.rate *= 0.5;
//設定音量
utterance.volume = 0.6;

[self.speechSynthesizer speakUtterance:utterance];複製程式碼

看上去很簡單的樣子,讓我們趕緊放進 extension 中試一下。
在之前的基礎上我們做一些修改,將播放操作封裝成一個播放的方法。


- (void)readContent:(NSString*)str{
    //AVSpeechUtterance: 可以假想成要說的一段話
    AVSpeechUtterance * aVSpeechUtterance = [[AVSpeechUtterance alloc] initWithString:str];

    aVSpeechUtterance.rate = AVSpeechUtteranceDefaultSpeechRate;

    //AVSpeechSynthesisVoice: 可以假想成人的聲音
    aVSpeechUtterance.voice =[AVSpeechSynthesisVoice voiceWithLanguage:@"zh-CN"];

    //發音
    [self.aVSpeechSynthesizer speakUtterance:aVSpeechUtterance];

}複製程式碼

target 配置

在收到推送的時候,將推送的 body 讀出來,想的還是美滋滋的。
把 demo 執行起來的時候,發現收到推送後並沒有聲音。
通過查閱資料,發現類似於這樣的後臺播放音樂,是需要一個 Background modes 的許可權的,就是下面的第一個 Mode。【不過好像有人說,勾選了該許可權可能會被拒】

iOS 10 來點不一樣的推送 (2) - 語音提示

音效不完整

然後我們再一次的嘗試,這次可以播放出聲音了,但是有個問題,就是聲音播放到一半就停了,然後緊跟著的是推送的通知音。初步推測是播放其實也是在另一個執行緒中的進行的,當結束 extension 的操作彈出通知時,播放語音仍在進行中,會導致兩個衝突,而系統通知的優先順序更高,所以原來的語音會被攔截。
這一步考慮的解決方法是,在 extension 中做一個延遲的操作,首先想到的是用 GCD 。

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)),
                   dispatch_get_main_queue(), ^{

        self.contentHandler(self.bestAttemptContent);

    });複製程式碼

假設播放的語音是 5秒,在呼叫播放 5 秒之後,再觸發處理完通知的回撥,這樣雖然是解決了上述的問題,但是似乎不夠的優雅,無法控制如果語音更長的情況。

自動結束

翻閱了文件,看看有沒有可以收到播放完成的事件的地方。發現 AVSpeechSynthesizer 有一個 AVSpeechSynthesizerDelegate,將當前的 NotificationService 實現 AVSpeechSynthesizerDelegate,這樣就能在下面的回撥中結束播放成功時間,這樣就能動態的控制通知展示的時間的。


- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance *)utterance;{

    NSLog(@"閱讀完畢");
    self.contentHandler(self.bestAttemptContent);
}複製程式碼

這樣就基本實現了我們想要的效果了!
由於推送的特殊性,可以實現後臺喚醒,所以當 app 執行在後臺,或者 app 被 kill 了,仍然可以喚醒並播放語言!?

合成

考慮的是採用科大訊飛的語音合成 SDK,在 extension 中進行整合,然後轉換成語音檔案儲存至本地,同時把推送的提示語音設定為該音訊檔案。
由於科大訊飛註冊太麻煩了,就沒嘗試(跑。。)。不過感覺理論上應該可以實現該功能,主要有問題的地方可能就是轉換之後的語言檔案是否能作為提示音的問題了。

總結

基本的功能已經實現,最新的程式碼已經提交到原先的 demo 中啦,但是需要注意的是 demo 不帶證照,可以把相關程式碼拷到你自己的專案中去嘗試,演示視訊也傳到 GitHub 中了。
(如果感到有用的加個✨吧 溜了 溜了)

相關文章