簡體   English   中英

如果以模態呈現另一個視圖,則保持 AVSpeechSynthesizer 播放

[英]Keep AVSpeechSynthesizer playing if another view is presented modally

我正在嘗試在 swift/xcode 中利用 AVSpeechSynthesizer 來讀出一些文本。 我大部分時間都在工作。 我已經設置好了,如果他們回到上一個屏幕或下一個屏幕,音頻將停止。 但在我的例子中,如果以模態呈現另一種觀點,我希望演講繼續。 例如,我有一個退出按鈕,點擊后會顯示“您確定要退出嗎?是/否”類型的屏幕,但我希望音頻繼續播放,直到他們單擊是並被帶走。 我還有另一個可以模態呈現的視圖,如果是這種情況,我希望音頻繼續。

有沒有人知道如何在視圖以模態顯示在頂部時保持語音播放,但在完全導航到另一個視圖時停止播放?

到目前為止,這是我的代碼:

//Press Play/Pause Button
@IBAction func playPauseButtonAction(_ sender: Any) {
    if(isPlaying){
        //pause
        synthesizer.pauseSpeaking(at: AVSpeechBoundary.immediate)
        playPauseButton.setTitle("Play", for: .normal)
    } else {
        if(synthesizer.isPaused){
            //resume playing
            synthesizer.continueSpeaking()
        } else {
            //start playing
            theUtterance = AVSpeechUtterance(string: audioTextLabel.text!)
            theUtterance.voice = AVSpeechSynthesisVoice(language: "en-UK")
            synthesizer.speak(theUtterance)
        }
        playPauseButton.setTitle("Pause", for: .normal)
    }
    isPlaying = !isPlaying
}

//Press Stop Button
@IBAction func stopButtonAction(_ sender: Any) {
    if(isPlaying){
        //stop
        synthesizer.stopSpeaking(at: AVSpeechBoundary.immediate)
        playPauseButton.setTitle("Play", for: .normal)
        isPlaying = !isPlaying
    }
}

//Leave Page
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    synthesizer.stopSpeaking(at: .immediate)
}

問題在於您的viewWillDisappear 任何類型的新屏幕都會觸發此操作,因此您的代碼確實會被調用synthesizer.stopSpeaking(at: .immediate) ,從而停止您的音頻。 這包括演示或推送新的控制器。

現在,如何改進? 你提到過這個:

我已經設置好了,如果他們回到上一個屏幕或下一個屏幕,音頻將停止

首先,如果他們回到上一個屏幕

您希望在deinit { }方法中執行相同的音頻代碼行停止。 這會讓您知道您的屏幕或控制器正在從內存中刪除,這意味着控制器在您的控制器堆棧中消失了(用戶返回到上一個屏幕)。 只要您沒有保留周期計數問題,這應該可以 100% 正常工作。

接下來,到下一個屏幕,您可以輕松地在函數中包含停止音頻的相同代碼行以推送新屏幕。

經過大量研究,我能夠獲得所需的功能。 正如格倫所建議的那樣,正確的方法是在deinit而不是viewWillDisappear調用deinit 問題是使用AVSpeechSynthesizer / AVSpeechSynthesizerDelegate通常會創建對 ViewController 的強引用,因此不會調用 deinit。 為了解決這個問題,我必須創建一個從AVSpeechSynthesizerDelegate繼承的自定義類,但使用對自定義協議的弱委托引用。

自定義類和協議:

import UIKit
import AVFoundation

protocol AudioTextReaderDelegate: AnyObject {
    func speechDidFinish()
}

class AudioTextReader: NSObject, AVSpeechSynthesizerDelegate {
    let synthesizer = AVSpeechSynthesizer()

    weak var delegate: AudioTextReaderDelegate!
    //^IMPORTANT to use weak so that strong reference isn't created.

    override init(){
        super.init()
        self.synthesizer.delegate = self
    }

    func startSpeaking(_ toRead: String){
        let utterance = AVSpeechUtterance(string: toRead)
        synthesizer.speak(utterance)
    }

    func resumeSpeaking(){
        synthesizer.continueSpeaking()
    }

    func pauseSpeaking(){
        synthesizer.pauseSpeaking(at: .immediate)
    }

    func stopSpeaking() {
        synthesizer.stopSpeaking(at: .immediate)
    }

    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
        self.delegate.speechDidFinish()
    }
}

然后在我的 ViewController 中繼承和使用它:

import UIKit
import AVFoundation

class MyClassViewController: UIViewController, AudioTextReaderDelegate{
    var isPlaying = false

    let audioReader = AudioTextReader()

    let toReadText = "This is the text to speak"

    @IBOutlet weak var playPauseButton: UIButton!
    @IBOutlet weak var stopButton: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()

        self.audioReader.delegate = self
    }

    //Press Play/Pause Button
    @IBAction func playPauseButtonAction(_ sender: Any) {
        if(isPlaying){
            //Pause
            audioReader.synthesizer.pauseSpeaking(at: .immediate)
            playPauseButton.setTitle("Play", for: .normal)
        } else {
            if(audioReader.synthesizer.isPaused){
                //Resume Playing
                audioReader.resumeSpeaking()
            } else {
                audioReader.startSpeaking(toReadText)
            }
            playPauseButton.setTitle("Pause", for: .normal)
        }
        isPlaying = !isPlaying
    }

    //Press Stop Button
    @IBAction func stopButtonAction(_ sender: Any) {
        if(isPlaying){
            //Change Button Text
            playPauseButton.setTitle("Play", for: .normal)
            isPlaying = !isPlaying
        }
        //Stop
        audioReader.stopSpeaking()
    }

    //Finished Reading
    func speechDidFinish() {
        playPauseButton.setTitle("Play", for: .normal)
        isPlaying = !isPlaying
    }

    //Leave Page
    deinit {
        audioReader.stopSpeaking()
        playPauseButton.setTitle("Play", for: .normal)
        isPlaying = !isPlaying
    }
    @IBAction func NextStopButton(_ sender: Any) {
        audioReader.stopSpeaking()
        playPauseButton.setTitle("Play", for: .normal)
        isPlaying = !isPlaying
    }
}

我希望這對未來的人有所幫助,因為這讓我難以自拔。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM