開不了口的告白就用AVSpeechSynthesizer說出口

連接起StoryBoard和程式的IBOutlet & IBAction

--

繼上一篇利用IBOutlet & IBAction建立起元件和程式間的橋樑,做出換圖片的效果,這篇是AVFoundation之AVSpeechSynthesizer的應用篇

本次運用到以下功能:

小小兵說話器

  • 點選UIStepper以固定數值調整說話音高、速度
  • 滑動UISlider調整說話音量
  • 讓UILabel的數值隨UIStepper和UISlider的數值變動
  • 點選UIButton念出預設的文字,並包含已設定好的屬性

藍色說話器

  • 讓AVSpeechSynthesizer的說話內容為UITextView填入的文字
  • 用if else控制UIButton說話的播放、暫停、停止
  • 點選UIButton收鍵盤
  • 用UISegmentedControl選擇說話語言
  • 滑動UISlider時對應的數值顯示在UILabel上
  • 用if else控制UISwitch開關的條件
  • 利用亂數隨機設定單一/全部說話屬性
  • 點選UIButton讓說話屬性回到初始設定
  • 利用String Interpolation(字串置換)印出數值
  • 學習呼叫拉了IBAction的funciton

元件外觀調整說明

View
我在小小兵說話器和藍色說話器的說話屬性的元件們下面墊了一個View,想用來凸顯調整屬性功能的區塊。然後試了兩種做法來調整View的外觀:

1. 小小兵在StoryBoard新增View元件後,從程式碼自訂function來設定View的圓角、邊框寬度和顏色後,在ViewDidLoad裡呼叫setViewAttribute(),讓View的外觀在app一啟動就顯現。

2. 藍色說話器則是直接在StoryBoard的Identity Inspector的 User Defined Runtime Attributes 幫View設定圓角,這個作法就不用用到viewDidLoad()。圓角效果同樣要在啟動app後才看的出來。

Switch

默認On顏色
默認Off顏色

在StoryBoard中調整Switch的外觀,Switch的紐和在On狀態下的底色可以透過onTint和ThumbTint設定。
尷尬的是Switch在默認的off狀態下貼在View上會消失。

設定後On
設定後Off

接著設定Background藍色,Switch卻變成長方形,所以我同樣在Identity Inspector的 User Defined Runtime Attributes 幫background設定和Switch默認相同大小的圓角15,解決了這個問題。

元件初始數值設定

UISlider和UIStepper的初始數值設定

在StoryBoard設定的元件數值(例如value、Maximum、Minimum、Step等)會為元件初始值,如果後續想要隨著event的觸發變更,就要寫在IBAction中。

而UISlider和UIStepper因為設定用來控制相關的聲音屬性,
所以是Maximum、Minimum是依照Utterance屬性的最大、最小值去設定的。

volume:音量,min-max(0–1)
pitchMultiplier:音高,min-max(0.5–2.0)
rate:語速,min-max(0–1.0)

關於AVFoundation的基礎應用可以參考下面文章

學習使用 AVSpeechSynthesizer 講話

小小兵說話器AVFoundation

說話功能相關的四個元件執行IBAction由上至下,每個func裡都用print在debug area顯示數值:

  • UIButton : 設定說英國腔後,執行說話speak
  • UIStepper : 調整音高 & 音高數值同步顯示在Label上
  • UIStepper:調整語速& 語速數值同步顯示在Label上
  • UISlider:調整音量& 音量數值同步顯示在Label上

因為四個元件的IBAction裡都有引用到常數sythesizerspeechUtterence
所以生成合成器let sythesizer = AVSpeechSynthesizer()和說話內容let speechUtterence = AVSpeechUtterance(string: “Bello I love banana “)要寫在IBAction的外面,如果寫在四個任一元件IBAction內的話,會有讀不到的問題。

藍色說話器AVFoundation

藍色說話器是微進階版,利用了更多元件來測試功能,並且將每個說話屬性儲存為ViewController的property,避免每次執行function後數值就被清掉。最後在func尾用把數值變字串的方式\( )print出ViewController的property數值,方便從Xcode的debug area確認每個func是否有跑出正確的結果。

功能設計相較小小兵說話器,藍色說話器為了選擇障礙的夥伴們新增了兩種隨機按鈕:一種針對單一屬性隨機調整UISlider數值、另一種針對全部屬性隨機調整UISlider數值,透過隨機鈕選到想要的數值並將其相對應的UISwitch切換至ON把數值固定住,就不用擔心動到已確認的值,可以放心的隨機選取剩下的屬性數值啦!

如果不想要隨機跑數字時也可以手動調整,與小小兵說話器在UIStepper跟UISlider被觸發後才改變數值不同,藍色說話器是先自訂function,其中包含說話的屬性。
合成器的說話的內容是TextView中輸入的文字,還新增了三種語言選項,在念出中、英,或日文中途可以隨時按暫停和停止。設定好說話屬性後,再執行說話speak。最後再playButton的IBAction裡呼叫自訂的function。

而三個UISlider的action都是被滑動時左側的Label同步顯示數值,所以可以拉到同一個@IBAction func changeLabelValue下。
關於Label顯示的格式,”%.2f”代表顯示小數點後兩位

此外,因為在程式前面宣告了class ViewController 屬性,用來存取所有元件func執行過程中的說話屬性數值。
var volumeValue = 0.5
var rateValue = 0.5
var pitchValue = 1.0
let language = ["zh-TW","en-US","ja-JP"]

var index = 0

所以在設定resetButton時,直接在他的func中將初始值再儲存給屬性就好。
(之後有機會用struct來定義新的型別後就不用像class要宣告一堆變數看起來落落長又很繁瑣)

除了透過點選resetButton回歸初始值以外,也要讓App一啟動就是初始狀態,所以ViewDidLoad的func裡也要呼叫resetButton的func updateAllValue()。
但因為是拉了IBAction的func不能按一般方式呼叫,但又不想再自訂一個一樣的function執行初始值,所以我找到了呼叫IBAction function的方式:

完整程式碼

專案下載

其他小小注意

  1. 匯入的函式庫import AVFoundation要寫在class ViewController: UIViewController的外面
    → 因為是整個檔案
    匯入AVFoundation的函式庫
  2. 說話的屬性們用合成器念出要說的話如果寫在同一個func下(藍色說話器的程式寫法),要注意說話的屬性們一定要寫在用合成器念出要說的話之前。
    → 如果合成器都講完話了才去設定講話屬性就來不及了!
    (當func越寫越多,這點會意外變成盲點要特別留意)

--

--