非常感謝大家利用自己寶貴的時(shí)間來(lái)閱讀我的文章 , ?上一篇文章寫(xiě)了Epub的加密一個(gè)實(shí)現(xiàn)方式,《ios Epub加密及解密閱讀的一種實(shí)現(xiàn)方式》,今天這篇文章主要說(shuō)一下電子書(shū)的語(yǔ)音閱讀叹洲,這個(gè)功能也基本上是閱讀器的標(biāo)配了离福。希望這篇文章能給你的開(kāi)發(fā)過(guò)程帶來(lái)一些幫助爬骤。喜歡的可以關(guān)注一下我的簡(jiǎn)書(shū)醋奠、我的博客 ??
demo下載地址ZQReaderDemo
關(guān)于電子書(shū)閱讀這這塊就不說(shuō)了,還是在XDSReader的基礎(chǔ)上修改耳峦,添加了語(yǔ)音閱讀的功能
在網(wǎng)上查了些資料恩静,IOS7.0之后系統(tǒng)添加了文字轉(zhuǎn)語(yǔ)音的API,AVFoundation框架下的AVSpeechSynthesizer類蹲坷,具體使用代碼如下驶乾,也可參考ZQReaderDemo中的ZQAVSpeechTool
//初始化語(yǔ)音合成器??
_avSpeaker?=?[[AVSpeechSynthesizer?alloc]?init];??
_avSpeaker.delegate?=?self;??
//初始化要說(shuō)出的內(nèi)容??
AVSpeechUtterance?*utterance?=?[[AVSpeechUtterance?alloc]?initWithString:_paragraphs[_currentParagraphs]];??
//設(shè)置語(yǔ)速,語(yǔ)速介于AVSpeechUtteranceMaximumSpeechRate和AVSpeechUtteranceMinimumSpeechRate之間??
//AVSpeechUtteranceMaximumSpeechRate??
//AVSpeechUtteranceMinimumSpeechRate??
//AVSpeechUtteranceDefaultSpeechRate??
utterance.rate?=?_rate;??
//設(shè)置音高,[0.5?-?2]?默認(rèn)?=?1??
//AVSpeechUtteranceMaximumSpeechRate??
//AVSpeechUtteranceMinimumSpeechRate??
//AVSpeechUtteranceDefaultSpeechRate??
utterance.pitchMultiplier?=?1;??
//設(shè)置音量,[0-1]?默認(rèn)?=?1??
utterance.volume?=?1;??
//讀一段前的停頓時(shí)間??
utterance.preUtteranceDelay?=?0.5;??
//讀完一段后的停頓時(shí)間??
utterance.postUtteranceDelay?=?0;??
//設(shè)置聲音,是AVSpeechSynthesisVoice對(duì)象??
//AVSpeechSynthesisVoice定義了一系列的聲音,?主要是不同的語(yǔ)言和地區(qū).??
//voiceWithLanguage:?根據(jù)制定的語(yǔ)言,?獲得一個(gè)聲音.??
//speechVoices:?獲得當(dāng)前設(shè)備支持的聲音??
//currentLanguageCode:?獲得當(dāng)前聲音的語(yǔ)言字符串,?比如”ZH-cn”??
//language:?獲得當(dāng)前的語(yǔ)言??
//通過(guò)特定的語(yǔ)言獲得聲音??
AVSpeechSynthesisVoice?*voice?=?[AVSpeechSynthesisVoice?voiceWithLanguage:@"zh-CN"];??
//通過(guò)voicce標(biāo)示獲得聲音??
//AVSpeechSynthesisVoice?*voice?=?[AVSpeechSynthesisVoice?voiceWithIdentifier:AVSpeechSynthesisVoiceIdentifierAlex];??
utterance.voice?=?voice;??
//開(kāi)始朗讀??
[_avSpeaker?speakUtterance:utterance];??
既然語(yǔ)音轉(zhuǎn)文字實(shí)現(xiàn)方法找到了,那么剩下的都是邏輯上的問(wèn)題了循签,最后整理了一下實(shí)現(xiàn)思路级乐,用戶在閱讀菜單上點(diǎn)擊語(yǔ)音按鈕,開(kāi)始語(yǔ)音閱讀當(dāng)前頁(yè)內(nèi)容县匠,首先取出當(dāng)前頁(yè)的所有文本唇牧,當(dāng)前頁(yè)讀完跳轉(zhuǎn)下一頁(yè)繼續(xù)閱讀,后考慮到高亮問(wèn)題聚唐,又把每一頁(yè)的文本拆分成段落進(jìn)行閱讀,當(dāng)讀完一段開(kāi)始讀下一段腔召,當(dāng)前頁(yè)讀完就繼續(xù)讀下一頁(yè)杆查,當(dāng)前章讀完就讀下一章,整體思路想好了就開(kāi)整臀蛛。
1亲桦、創(chuàng)建了一個(gè)單例工具類ZQAVSpeechTool崖蜜,代碼如下
.h
#import???
@interface?ZQAVSpeechTool?:?NSObject??
+(instancetype)shareSpeechTool;??
//開(kāi)始朗讀??
-?(void)speechTextWith:(NSString?*)text;??
//暫停朗讀??
-?(void)pauseSpeech;??
//繼續(xù)朗讀??
-?(void)continueSpeech;??
//結(jié)束朗讀??
-?(void)StopSpeech;??
//切換語(yǔ)速??
-?(void)changeRate:(CGFloat)rate;??
@end??
.m
#import?"ZQAVSpeechTool.h"??
#import???
@interface?ZQAVSpeechTool?()??
@property?(nonatomic?,strong)AVSpeechSynthesizer?*avSpeaker;??
@property?(nonatomic?,strong)NSArray?*paragraphs;??
@property?(nonatomic?,assign)NSInteger?currentParagraphs;??
@property?(nonatomic?,assign)CGFloat?rate;??
@end??
@implementation?ZQAVSpeechTool??
//?單例??
+(instancetype)shareSpeechTool?{??
static?ZQAVSpeechTool?*instance;??
static?dispatch_once_t?onceToken;??
????dispatch_once(&onceToken,?^{??
instance?=?[[ZQAVSpeechTool?alloc]init];??
????});??
return?instance;??
}??
-?(void)speechTextWith:(NSString?*)text??
{??
if?(!(text.length>0))?{??
return;??
????}??
if?(_avSpeaker)?{??
//把每一頁(yè)文字拆分成段??
_paragraphs?=[text?componentsSeparatedByString:@"\n"];??
_currentParagraphs?=0;??
[self?speechParagraphWith:_paragraphs[_currentParagraphs]];??
}else??
????{??
//初次閱讀??
NSUserDefaults?*useDef?=?[NSUserDefaults?standardUserDefaults];??
_rate?=?[useDef?floatForKey:@"speechRate"];??
if?(!_rate)?{??
_rate?=0.5;??
????????}??
_paragraphs?=[text?componentsSeparatedByString:@"\n"];??
_currentParagraphs?=0;??
[[NSNotificationCenter?defaultCenter]?postNotificationName:@"speechParagraph"?object:_paragraphs[_currentParagraphs]];??
//初始化語(yǔ)音合成器??
_avSpeaker?=?[[AVSpeechSynthesizer?alloc]?init];??
_avSpeaker.delegate?=?self;??
//初始化要說(shuō)出的內(nèi)容??
AVSpeechUtterance?*utterance?=?[[AVSpeechUtterance?alloc]?initWithString:_paragraphs[_currentParagraphs]];??
//設(shè)置語(yǔ)速,語(yǔ)速介于AVSpeechUtteranceMaximumSpeechRate和AVSpeechUtteranceMinimumSpeechRate之間??
//AVSpeechUtteranceMaximumSpeechRate??
//AVSpeechUtteranceMinimumSpeechRate??
//AVSpeechUtteranceDefaultSpeechRate??
utterance.rate?=?_rate;??
//設(shè)置音高,[0.5?-?2]?默認(rèn)?=?1??
//AVSpeechUtteranceMaximumSpeechRate??
//AVSpeechUtteranceMinimumSpeechRate??
//AVSpeechUtteranceDefaultSpeechRate??
utterance.pitchMultiplier?=?1;??
//設(shè)置音量,[0-1]?默認(rèn)?=?1??
utterance.volume?=?1;??
//讀一段前的停頓時(shí)間??
utterance.preUtteranceDelay?=?0.5;??
//讀完一段后的停頓時(shí)間??
utterance.postUtteranceDelay?=?0;??
//設(shè)置聲音,是AVSpeechSynthesisVoice對(duì)象??
//AVSpeechSynthesisVoice定義了一系列的聲音,?主要是不同的語(yǔ)言和地區(qū).??
//voiceWithLanguage:?根據(jù)制定的語(yǔ)言,?獲得一個(gè)聲音.??
//speechVoices:?獲得當(dāng)前設(shè)備支持的聲音??
//currentLanguageCode:?獲得當(dāng)前聲音的語(yǔ)言字符串,?比如”ZH-cn”??
//language:?獲得當(dāng)前的語(yǔ)言??
//通過(guò)特定的語(yǔ)言獲得聲音??
AVSpeechSynthesisVoice?*voice?=?[AVSpeechSynthesisVoice?voiceWithLanguage:@"zh-CN"];??
//通過(guò)voicce標(biāo)示獲得聲音??
//AVSpeechSynthesisVoice?*voice?=?[AVSpeechSynthesisVoice?voiceWithIdentifier:AVSpeechSynthesisVoiceIdentifierAlex];??
utterance.voice?=?voice;??
//開(kāi)始朗讀??
[_avSpeaker?speakUtterance:utterance];??
????}??
}??
-?(void)speechParagraphWith:(NSString?*)Paragraph??
{??
[[NSNotificationCenter?defaultCenter]?postNotificationName:@"speechParagraph"?object:Paragraph];??
AVSpeechUtterance?*utterance?=?[AVSpeechUtterance?speechUtteranceWithString:Paragraph];??
utterance.rate?=?_rate;??
[_avSpeaker?speakUtterance:utterance];??
}??
//切換語(yǔ)速??
-?(void)changeRate:(CGFloat)rate??
{??
????_rate?=?rate;??
//??
[_avSpeaker?stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];????//初始化語(yǔ)音合成器??
//????_avSpeaker?=?[[AVSpeechSynthesizer?alloc]?init];??
//????_avSpeaker.delegate?=?self;??
AVSpeechUtterance?*utterance?=?[AVSpeechUtterance?speechUtteranceWithString:_paragraphs[_currentParagraphs]];??
utterance.rate?=?rate;??
utterance.pitchMultiplier?=?1;??
utterance.volume?=?1;??
utterance.preUtteranceDelay?=?0.5;??
utterance.postUtteranceDelay?=?0;??
[_avSpeaker?speakUtterance:utterance];??
NSUserDefaults?*useDef?=?[NSUserDefaults?standardUserDefaults];??
[useDef?setFloat:rate?forKey:@"speechRate"];??
[useDef?synchronize];??
}??
-?(void)pauseSpeech??
{??
//暫停朗讀??
//AVSpeechBoundaryImmediate?立即停止??
//AVSpeechBoundaryWord????當(dāng)前詞結(jié)束后停止??
[_avSpeaker?pauseSpeakingAtBoundary:AVSpeechBoundaryImmediate];??
}??
-?(void)continueSpeech??
{??
[_avSpeaker?continueSpeaking];??
}??
-?(void)StopSpeech??
{??
//AVSpeechBoundaryImmediate?立即停止??
//AVSpeechBoundaryWord????當(dāng)前詞結(jié)束后停止??
[_avSpeaker?stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];??
_avSpeaker?=?nil;??
[XDSReadManager?sharedManager].speeching?=?NO;??
[[NSNotificationCenter?defaultCenter]?postNotificationName:@"speechDidStop"?object:nil];??
}??
#pragma?mark?-??
#pragma?mark?-?AVSpeechSynthesizerDelegate??
//已經(jīng)開(kāi)始??
-?(void)speechSynthesizer:(AVSpeechSynthesizer?*)synthesizer?didStartSpeechUtterance:(AVSpeechUtterance?*)utterance{??
}??
//已經(jīng)說(shuō)完??
-?(void)speechSynthesizer:(AVSpeechSynthesizer?*)synthesizer?didFinishSpeechUtterance:(AVSpeechUtterance?*)utterance{??
_currentParagraphs+=1;??
if?(_currentParagraphs<_paragraphs.count)?{??
//讀下一段??
[self?speechParagraphWith:_paragraphs[_currentParagraphs]];??
}else{??
NSInteger?currentPage?=?CURRENT_RECORD.currentPage;??
NSInteger?currentChapter?=?CURRENT_RECORD.currentChapter;??
if?(currentPage?<?CURRENT_RECORD.totalPage?-?1)?{??
//下一頁(yè)??
currentPage?+=1;??
}else??
????{??
if?(currentChapter?<?CURRENT_RECORD.totalChapters?-?1)?{??
//下一章??
currentChapter?+=1;??
currentPage?=0;??
}else??
????????{??
//全書(shū)讀完??
[self?StopSpeech];??
return;??
????????}??
????}??
[[XDSReadManager?sharedManager]?readViewJumpToChapter:currentChapter?page:currentPage];??
NSString?*content?=?CURRENT_RECORD.chapterModel.pageStrings[currentPage];??
[self?speechTextWith:content];??
????}??
}??
//已經(jīng)暫停??
-?(void)speechSynthesizer:(AVSpeechSynthesizer?*)synthesizer?didPauseSpeechUtterance:(AVSpeechUtterance?*)utterance{??
}??
//已經(jīng)繼續(xù)說(shuō)話??
-?(void)speechSynthesizer:(AVSpeechSynthesizer?*)synthesizer?didContinueSpeechUtterance:(AVSpeechUtterance?*)utterance{??
}??
//已經(jīng)取消說(shuō)話??
-?(void)speechSynthesizer:(AVSpeechSynthesizer?*)synthesizer?didCancelSpeechUtterance:(AVSpeechUtterance?*)utterance{??
}??
//將要說(shuō)某段話??
-?(void)speechSynthesizer:(AVSpeechSynthesizer?*)synthesizer?willSpeakRangeOfSpeechString:(NSRange)characterRange?utterance:(AVSpeechUtterance?*)utterance{??
//????DebugLog(@"%@",utterance.speechString);??
}??
@end??
2、為XDSReadManager添加開(kāi)始語(yǔ)音閱讀的方法客峭,并添加一個(gè)用于判斷當(dāng)前閱讀狀態(tài)的屬性
-?(void)begainSpeech;??
@property?(nonatomic?,assign)BOOL?speeching;??
方法的實(shí)現(xiàn)
//開(kāi)始語(yǔ)音閱讀??
-?(void)begainSpeech??
{??
//取出當(dāng)前頁(yè)文字內(nèi)容??
XDSChapterModel?*currentChapterModel?=?_bookModel.record.chapterModel;??
NSInteger?currentPage?=?_bookModel.record.currentPage;??
NSString?*content?=?currentChapterModel.pageStrings[currentPage];??
//開(kāi)始朗讀本頁(yè)文字??
[[ZQAVSpeechTool?shareSpeechTool]?speechTextWith:content];??
self.speeching?=?YES;??
}??
3豫领、在彈出菜單時(shí)判斷是否正在朗讀,是則彈出語(yǔ)音閱讀菜單ZQSpeechMenuView,因?yàn)锳VSpeechSynthesizer只有女聲舔琅,所以菜單只是加了調(diào)節(jié)語(yǔ)速和暫停/播放等恐、退出的功能,沒(méi)有添加切換男女聲的功能
主要功能實(shí)現(xiàn)
//暫停/播放??
-?(void)pauseBtnClick:(UIButton?*)sender??
{??
if?(_pauseBtn.selected)?{??
[[ZQAVSpeechTool?shareSpeechTool]?continueSpeech];??
}else{??
[[ZQAVSpeechTool?shareSpeechTool]?pauseSpeech];??
????}??
_pauseBtn.selected?=?!_pauseBtn.selected;??
}??
//退出??
-?(void)stopBtnClick??
{??
_pauseBtn.selected?=?NO;??
[[ZQAVSpeechTool?shareSpeechTool]?StopSpeech];??
[self?removeFromSuperview];??
}??
//切換語(yǔ)速??
-?(void)sliderValueChanged:(UISlider?*)slider{??
[[ZQAVSpeechTool?shareSpeechTool]?changeRate:slider.value];??
_pauseBtn.selected?=?NO;??
}??
4备蚓、高亮正在閱讀段落
-?(void)highlightTextWith:(NSString?*)text{??
//移除上一段背景色??
if?(_currentRange.length>0)?{??
[_readAttributedContent?removeAttribute:NSBackgroundColorAttributeName?range:_currentRange];??
????}??
//設(shè)置當(dāng)前朗讀段落背景色??
NSRange?range?=?[_content?rangeOfString:text];??
????_currentRange?=?range;??
[_readAttributedContent?addAttribute:NSBackgroundColorAttributeName?value:RGB(150,?220,?240)?range:range];??
self.readAttributedContent?=?_readAttributedContent;??
[self?reloadView];??
}??
大工搞成课蔬,具體的實(shí)現(xiàn)邏輯及代碼見(jiàn)ZQReaderDemo,語(yǔ)音菜單界面約束使用的SDAutoLayout郊尝,可以根據(jù)自己習(xí)慣進(jìn)行更改二跋。