如果以其他方式呈现其他视图,则保持AVSpeechSynthesizer播放

时间:2019-05-02 19:19:21

标签: ios swift xcode

我试图在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)
}

2 个答案:

答案 0 :(得分:1)

问题出在您的viewWillDisappear上。任何一种新的屏幕都会触发此操作,因此实际上将调用您的代码synthesizer.stopSpeaking(at: .immediate),从而停止音频。其中包括演示或推送新控制器。

现在,如何改善它?您已经提到了这一点:

  

我对其进行了设置,以便它们返回上一个屏幕或   在下一屏幕上,音频将停止

首先,如果他们返回上一个屏幕

您想在您的内部执行相同的音频代码行停止 deinit { }方法。这将使您知道屏幕或控制器已从内存中删除,这意味着控制器已在控制器堆栈中移走(用户返回到上一屏幕)。只要您没有保留周期计数问题,此方法就可以100%正常工作。

接下来,到下一个屏幕,您可以轻松地在停止推送新屏幕的功能中包含相同的代码行。

答案 1 :(得分:0)

经过大量研究,我能够获得所需的功能。正如Glenn所建议的那样,正确的方法是在deinit中而不是viewWillDisappear中调用stopSpeaking。问题是通常使用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
    }
}

我希望这对以后的人有所帮助,因为这使我无所适从。