#16 愛要大聲說&唸出動漫角色
目的:學習使用 AVSpeechSynthesizer 講話&搭配PageControl更換角色
APP製作說明
- 在TextField輸入文字,按下播放鍵播放內容
- 利用Slider調整音調、語速及音量
- 定義一個亂數button,可隨機調整音調、語速及音量
- 利用if-else更換Utterance的String內容
- 搭配PageControl實現換頁功能
- 搭配SegmentControl切換語系
Storyboard拉好畫面後,將需要的元件連上@IBOutlet
播放Button
utterance裡的String輸入textField.text!
可以讀取textField裡的內容,slider.value
可以讀出slider的值,.voice
可以更改語言,最後建立synthesizer常數儲存AVSpeechSynthesizer,.speak
就可以說出你想說的話了!
@IBAction func playBtn(_ sender: Any) {
let utterance = AVSpeechUtterance(string: textField.text!)
utterance.rate = rateSlider.value
utterance.pitchMultiplier = pitchSlider.value
utterance.volume = volumeSlider.value
utterance.voice = AVSpeechSynthesisVoice(language: "zh-TW")
let synthesizer = AVSpeechSynthesizer()
synthesizer.speak(utterance)
}
亂數Button
.random(in: 0…1)
代表會從0到1之間隨機選出一個值,.setValue
可以讓Slider在按下button時有移動的動畫
@IBAction func random(_ sender: Any) {
rateSlider.setValue(.random(in: 0...1), animated: true)
pitchSlider.setValue(.random(in: 0.5...2), animated: true)
volumeSlider.setValue(.random(in: 0...1), animated: true)
}
唸出動漫角色名的Button
這裡一樣會用到AVSpeechUtterance&AVSpeechSynthesizer來讓button按下時會說話,那如何讓button說出不一樣的內容呢,利用if-else判斷實現說出不同內容的button
.selectedSegmentIndex ==0
為左邊數來第一個,那在這個位置定義為中文,假設segment為中文的話則說出中文角色名,segment為日文的話則說出日文角色名,接著用圖片來判斷說出對應的角色名,這邊看似很複雜,if 裡面又包著if,但理解過後邏輯其實很簡單
@IBAction func animateName(_ sender: UIButton) {
var utterence = AVSpeechUtterance()
if segmentControl.selectedSegmentIndex == 0 {
if imageView.image == UIImage(named: animateImage[0]) {
utterence = AVSpeechUtterance(string: "旋渦鳴人")
} else if imageView.image == UIImage(named: animateImage[1]) {
utterence = AVSpeechUtterance(string: "宇智波佐助")
} else if imageView.image == UIImage(named: animateImage[2]) {
utterence = AVSpeechUtterance(string: "春野櫻")
} else if imageView.image == UIImage(named: animateImage[3]) {
utterence = AVSpeechUtterance(string: "旗木卡卡西")
} else if imageView.image == UIImage(named: animateImage[4]) {
utterence = AVSpeechUtterance(string: "邁特凱")
} else if imageView.image == UIImage(named: animateImage[5]) {
utterence = AVSpeechUtterance(string: "宇智波帶土")
} else if imageView.image == UIImage(named: animateImage[6]) {
utterence = AVSpeechUtterance(string: "宇智波鼬")
} else if imageView.image == UIImage(named: animateImage[7]) {
utterence = AVSpeechUtterance(string: "宇智波斑")
} else if imageView.image == UIImage(named: animateImage[8]) {
utterence = AVSpeechUtterance(string: "自來也")
}
utterence.voice = AVSpeechSynthesisVoice(language: "zh-TW")
}
if segmentControl.selectedSegmentIndex == 1 {
if imageView.image == UIImage(named: animateImage[0]) {
utterence = AVSpeechUtterance(string: "なると")
} else if imageView.image == UIImage(named: animateImage[1]) {
utterence = AVSpeechUtterance(string: "うちはサスケ")
} else if imageView.image == UIImage(named: animateImage[2]) {
utterence = AVSpeechUtterance(string: "春野サクラ")
} else if imageView.image == UIImage(named: animateImage[3]) {
utterence = AVSpeechUtterance(string: "はたけカカシ")
} else if imageView.image == UIImage(named: animateImage[4]) {
utterence = AVSpeechUtterance(string: "マット・ケイ")
} else if imageView.image == UIImage(named: animateImage[5]) {
utterence = AVSpeechUtterance(string: "うちはオビト")
} else if imageView.image == UIImage(named: animateImage[6]) {
utterence = AVSpeechUtterance(string: "うちはイタチ")
} else if imageView.image == UIImage(named: animateImage[7]) {
utterence = AVSpeechUtterance(string: "うちはマダラ")
} else if imageView.image == UIImage(named: animateImage[8]) {
utterence = AVSpeechUtterance(string: "自来也")
}
utterence.voice = AVSpeechSynthesisVoice(language: "ja-JP")
}
let synthesizer = AVSpeechSynthesizer()
synthesizer.speak(utterence)
}
PageControl
.currentPage
可以知道是在第幾個點上,把它存在index讓圖片跟文字可以同步更新到
@IBAction func pageControl(_ sender: UIPageControl) {
index = sender.currentPage
imageView.image = UIImage(named: animateImage[index])
animateLabel.text = animateName[index]
}
上下頁Button&手勢
要做上下頁很直覺會想到index = index + 1
遞增,或者index = index — 1
遞減,但如果一直遞增或遞減下去會超出array的範圍,我們期望的是在最後一頁再按下下一頁button時會跳回第一頁的位置,這時候餘數%
可以解決超出範圍的問題,animateImage.count
可以取得array裡有幾筆資料,array裡的資料為9筆,除以9的餘數範圍為0~8,所以不管怎麼加都不會超過array的範圍,但遞減有個問題是,當減到index = 0
之後會變成負數,如果要讓它永遠為正數的話必須寫成index = (index + animateImage.count — 1) % animateImage.count
手勢function的跟button一模一樣,要注意的點是手勢要自己設定滑的方向,以及滑動的物件,在這裡會將手勢拖一到imageView上,imageView的User Interaction Enabled
記得打勾
@IBAction func next(_ sender: Any) {index = (index + 1) % animateImage.countimageView.image = UIImage(named: animateImage[index])animateLabel.text = animateName[index]animatePageControl.currentPage = index}@IBAction func pre(_ sender: Any) {index = (index + animateImage.count - 1) % animateImage.countimageView.image = UIImage(named: animateImage[index])animateLabel.text = animateName[index]animatePageControl.currentPage = index}@IBAction func nextSwipe(_ sender: UISwipeGestureRecognizer) {index = (index + 1) % animateImage.countimageView.image = UIImage(named: animateImage[index])animateLabel.text = animateName[index]animatePageControl.currentPage = index}@IBAction func preSwipe(_ sender: UISwipeGestureRecognizer) {index = (index + animateImage.count - 1) % animateImage.countimageView.image = UIImage(named: animateImage[index])animateLabel.text = animateName[index]animatePageControl.currentPage = index}
Demo