標籤:style http color 使用 strong os
原文: http://www.raywenderlich.com/64623/make-narrated-book-using-avspeechsynthesizer-ios-7
隨著 PageViewController 的引入,蘋果讓開發人員們製作圖書類app 更加輕鬆。不幸的是,對於生活在朝九晚五繁忙節奏中的人們來說,閱讀也是一件奢侈的事情。為什麼你不能在讀一本小說的同時做其他事情呢?
在 Siri 剛開始出現的時候,蘋果曾經用複雜的動態文本閱讀將開發人員拒之門外,但當iOS7 發布的時候,蘋果終於放開了這扇大門。
在本教程中,你將製作一本故事書。這本書的每一頁都會在顯示文字的同時朗讀文字中的內容。有聲的閱讀將讓你的 app 在 iTunes 中顯得與眾不同,同時還保護了視力。有聲書尤其受廣播聽眾的喜愛,因為它允許人們在鍛煉、烹飪或工作的同時進行“閱讀”。
當你製作自己的有聲書時, 你將學習到:
- 如何使用 AVSpeechSynthesizer 和 AVSpeechUtterance 讓 iOS 裝置朗讀文本
- How to make this synthesized speech sound more natural by modifying AVSpeechUtterance properties like pitch and rate.
- 如何修改 AVSpeechUtterance 屬性例如 pitch 和 rate,使合成的語音更自然
AVSpeechSynthesizer當然比不上真人語音,但它對於你將要開發的 app 來說,相對容易一些。
注意:關於如何用 Sprite Kit 開發iPad兒童書籍,請參考Tammy Coron 的教程: How to Create an Interactive Children’s Book for the iPad
開始:AVSpeechSynthesizer
首先,請下載 初始項目。進入NarratedBookUsingAVSpeechStarter 目錄,雙擊 NarratedBookUsingAVSpeech.xcodeproj 以開啟初始項目。
Build & run 。你將在模擬器中看到:
書的內容是關於松鼠的童謠。雖然不是亞馬遜買得最火的讀物,但對於本教程來說足夠了。向左滑動進行向後翻頁,向右滑動則返回前一頁。噢,它已經擁有了基本的“書”的功能,真是不錯的開始。
理解機制
注意:教程的最後,會留給你幾個習題。接下來一節將包括樣本項目的一些內容,以便你能獨立完成這些習題。如果你對這部分內容不感興趣,請跳到下一節。
初始項目包括兩個類:
1. Models: 用於存放書籍的內容,它是page 的集合。
2. Presentation: 將 models 展現到螢幕並響應使用者動作(例如滑動手勢)。
在你製作自己的圖書時,理解這兩個類的工作機制是很有必要的。開啟RWTBook.h:
@interface RWTBook : NSObject //1 @property (nonatomic, copy, readonly) NSArray *pages; //2 + (instancetype)bookWithPages:(NSArray*)pages; //3 + (instancetype)testBook; @end |
- pages 屬性存放了 Page 對象的數組,每個 Page對象代表圖書中的每一頁。
- bookWithPages: 方法是一個初始化 Book 的方法,它用指定的 page 對象數組為參數,返回一個 book 對象。
- testBook 建立 Book 對象,用於測試。在開始加入和讀取你自己的圖書內容之前,就先使用 testBook 建立一個簡單的 Book 吧。
RWTPage.h聲明如下:
//1 extern NSString* const RWTPageAttributesKeyUtterances; extern NSString* const RWTPageAttributesKeyBackgroundImage; @interface RWTPage : NSObject //2 @property (nonatomic, strong, readonly) NSString *displayText; @property (nonatomic, strong, readonly) UIImage *backgroundImage; //3 + (instancetype)pageWithAttributes:(NSDictionary*)attributes; @end |
- 常量用於從字典中檢索頁。RWTPageAttributesKeyUtterances常量可以檢索出page 對象中的文本,RWTPageAttributesKeyBackgroundImage則用於檢索 page 對象所用的背景圖片。
- displayText 屬性用於儲存 page 的文本,backgroundImage 屬性用於儲存 page 的背景圖片。
- pageWithAttributes:用指定的 NSDictionary 建立一個 page 執行個體。
RWTPageViewController.m聲明如下:
#pragma mark - Class Extension // 1 @interface RWTPageViewController () @property (nonatomic, strong) RWTBook *book; @property (nonatomic, assign) NSUInteger currentPageIndex; @end @implementation RWTPageViewController #pragma mark - Lifecycle // 2 - (void)viewDidLoad { [super viewDidLoad]; [self setupBook:[RWTBook testBook]]; UISwipeGestureRecognizer *swipeNext = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(gotoNextPage)]; swipeNext.direction = UISwipeGestureRecognizerDirectionLeft; [self.view addGestureRecognizer:swipeNext]; UISwipeGestureRecognizer *swipePrevious = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(gotoPreviousPage)]; swipePrevious.direction = UISwipeGestureRecognizerDirectionRight; [self.view addGestureRecognizer:swipePrevious]; } #pragma mark - Private // 3 - (RWTPage*)currentPage { return [self.book.pages objectAtIndex:self.currentPageIndex]; } // 4 - (void)setupBook:(RWTBook*)newBook { self.book = newBook; self.currentPageIndex = 0; [self setupForCurrentPage]; } // 5 - (void)setupForCurrentPage { self.pageTextLabel.text = [self currentPage].displayText; self.pageImageView.image = [self currentPage].backgroundImage; } // 6 - (void)gotoNextPage { if ([self.book.pages count] == 0 || self.currentPageIndex == [self.book.pages count] - 1) { return; } self.currentPageIndex += 1; [self setupForCurrentPage]; } // 7 - (void)gotoPreviousPage { if (self.currentPageIndex == 0) { return; } self.currentPageIndex -= 1; [self setupForCurrentPage]; } @end |
以上代碼說明如下:
- book 屬性儲存了當前的 RWTBook 對象,currentPageIndex屬性儲存了 RWTBook 對象的當前頁索引。
- 當視圖載入完畢,設定要顯示的 page,並添加滑動手勢的辨識器以便使用者能通過手勢進行翻頁。
- 返回當前頁的 RWTPage 對象。
- 設定 book 屬性並將當前頁置為第一頁。
- 設定當前頁的顯示內容。
- 尋找下一頁,如果該頁存在,則將下一頁設定為當前頁。該方法由 swipeNext 手勢辨識器調用。
- 尋找上一頁,如果該頁存在,則將上一頁設定為當前頁。該方法由 swipePrevious 手勢辨識器調用。
播放和停止!
這是一個很要命的問題。
開啟RWTPageViewController.m,在#import "RWTPage.h" 下面加入:
iOS 語音功能由 AVFoundation 架構提供,你必須匯入這個架構。
提示: @import會匯入並串連 AVFoundation 架構。關於 iOS7 中 @import 及相關的 O-C 語言新特性,請參考這篇文章What’sNew in Objective-C and Foundation in iOS 7。
在 currentPageIndex 屬性聲明之下加入:
@property (nonatomic, strong) AVSpeechSynthesizer *synthesizer; |
synthesizer 對象將用於朗讀每一頁中的文字。
可以將 ViewController 中定義的AVSpeechSynthesizer 對象想象成一個會說話的人。而 AVSpeechUtterance 則可以想象成一張小紙條,把紙條遞給這個人,則他就會念出紙條上的字。
注意:一個 AVSpeechUtterance 可能是一個單詞,比如“Whisky”,或者是一個完整的語句,比如“Whisky,frisky,hippidityhop”。
在 RWTPageViewController.m 的最後加入以下方法:
#pragma mark - Speech Management - (void)speakNextUtterance { AVSpeechUtterance *nextUtterance = [[AVSpeechUtterance alloc] initWithString:[self currentPage].displayText]; [self.synthesizer speakUtterance:nextUtterance]; } |
建立了一個 utterance 對象,然後告訴 synthesizer 去念出它。
然後實現這個方法:
- (void)startSpeaking { if (!self.synthesizer) { self.synthesizer = [[AVSpeechSynthesizer alloc] init]; } [self speakNextUtterance]; } |
這個方法負責初始化 synthesizer 屬性(如果它未初始化的話)。然後調用speakNextUtterance 方法,開始朗讀。
在 viewDidLoad 、gotoNextPage 和 gotoPreviousPage 方法的最後加上這行:
這樣,當書一開啟,或者使用者前後翻頁的時候,朗讀就會開始。
Build & run,你會聽到AVSpeechSynthesizer 發出的天籟之音。
注意:如果你什麼也沒聽到,請檢查 Mac 或者 iOS 裝置的音量設定(看你是在什麼地方運行這個 app 的)。你可以嘗試著進行翻頁看是不是能播放語音。
提示:如果你是在模擬器上運行程式, 可能控制台會輸出一堆莫名其妙的錯誤資訊。這隻會在模擬器上出現,使用裝置時則不會列印這些錯誤。
如果你聽到了語音播放,請再次 Build & Run。這次,在第一頁內容播放完之前,嘗試向左滑動(向後翻頁)。發現了什嗎?
synthesizer 只會在第一頁念完之後才開始念下一頁。這不是使用者想要的結果,他們會想讓第一頁停止播放而第二頁立即開始。這點小瑕疵對於一頁內容比較短的童謠來說不成問題,但試想一下,如果每頁的內容都很長的話會是什麼效果……