AVSpeechSynthesizer isSpeaking not working in Swift
It is just not updated in your case, because synthesiser.isSpeaking
is not observable for SwiftUI. You have to use view model class and delegate callbacks to handle this state changes
Here is initial demo (you can add stop/pause/continue actions by yourself)
class SpeachViewModel: NSObject, ObservableObject, AVSpeechSynthesizerDelegate {
@Published var isSpeaking = false
private var synthesizer = AVSpeechSynthesizer()
override init() {
super.init()
synthesizer.delegate = self
}
deinit {
synthesizer.delegate = nil
}
func speak(_ utterance: AVSpeechUtterance) {
self.synthesizer.speak(utterance)
}
// MARK: AVSpeechSynthesizerDelegate
internal func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didStart utterance: AVSpeechUtterance) {
self.isSpeaking = true
}
internal func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
self.isSpeaking = false
}
internal func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didPause utterance: AVSpeechUtterance) {
self.isSpeaking = false
}
internal func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didCancel utterance: AVSpeechUtterance) {
self.isSpeaking = false
}
internal func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didContinue utterance: AVSpeechUtterance) {
self.isSpeaking = true
}
}
struct ContentView: View {
@ObservedObject var vm = SpeachViewModel()
var utterance = AVSpeechUtterance(string: "Hello World")
var body: some View {
VStack {
Text(vm.isSpeaking ? "Speaking" : "Not Speaking")
Button(action: {vm.speak(utterance)}) {
Text("Speak To Me")
}
}
}
}
AVSpeechSynthesizer isn't working under ios16 anymore
u need to assign a delegate
class Speaker: NSObject, AVSpeechSynthesizerDelegate {
let synthesizer = AVSpeechSynthesizer()
init() {
synthesizer.delegate = self
}
func speak(msg: String) {
let utterance = AVSpeechUtterance(string: msg)
utterance.rate = 0.57
utterance.pitchMultiplier = 0.8
utterance.postUtteranceDelay = 0.2
utterance.volume = 0.8
let voice = AVSpeechSynthesisVoice(language: "en-US")
utterance.voice = voice
synthesizer.speak(utterance)
}
}
now is how to use
let s = Speaker()
s.speak(msg: "The quick brown fox jumped over the lazy dog.")
AVSpeechSynthesizer - If AVSpeechSynthesizer is Speaking & if has stopped speaking
A quick look at the docs for AVSpeechSynthesizer
reveals that it has a delegate
property.
You should set the delegate
and implement the AVSpeechSynthesizerDelegate
protocol so that you can be notified of events for the speech synthesizer.
Events such as
- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer
didFinishSpeechUtterance:(AVSpeechUtterance *)utterance;
and
- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer
didStartSpeechUtterance:(AVSpeechUtterance *)utterance;
would be most interesting to you, considering you want to know when it starts and stops. There are also events for being canceled, paused, and continued, which you will probably also want to implement to hide and show your UI.
iOS - AVSpeechSynthesizer pause and continueSpeaking issue
I implemented the following code to check your problem (testings under Mojave 10.14.4
, iOS 12.2
, Xcode 10.2.1
and swift 5.0
):
class SpeechSynthesis: UIViewController {
var synthesizer = AVSpeechSynthesizer()
var playQueue = [AVSpeechUtterance]()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
for i in 1...10 {
let stringNb = "number " + String(i) + " of the speech synthesizer."
let utterance = AVSpeechUtterance(string: stringNb)
playQueue.append(utterance)
}
for utterance in playQueue {
synthesizer.speak(utterance)
}
}
@IBAction func pauseButton(_ sender: UIButton) {
if (synthesizer.isSpeaking == true) {
if (synthesizer.pauseSpeaking(at: .immediate) == true) {
print("PAUSE")
} else {
print("P.R.O.B.L.E.M. when pausing.")
}
}
}
@IBAction func resumeButton(_ sender: UIButton) {
if (synthesizer.isPaused == true) {
if (synthesizer.continueSpeaking() == true) {
print("CONTINUE")
} else {
print("P.R.O.B.L.E.M. when resuming.")
}
}
}
}
I noticed another problem with the boundary .word
that doesn't always pause when triggered but when this constraint is changed to .immediate
, everything is resuming from where it paused.
However, when it rarely pauses with the boundary .word
, it always resumes from where it paused as well.
I don't know where your problem comes from but with the configuration mentioned above and this code snippet, the speech synthesizer resumes from where it paused.
Related Topics
Cannot Use Mutating Member ... Because Append
User Already Authenticated in App and Firebase Realtime Database Returns Failed: Permission_Denied
How to Pass Data from Using Post/Form Leaf Template
Swift - Connect Delegate to Custom Xib Cell
Nskeyedunarchiver Decodeobjectforkey: Cannot Decode Object of Class for Key (Ns.Objects)
Why Does a Function Have Long-Term Write Access to All of Its In-Out Parameters
Uiview.Animatewithduration Not Animating
Swift Flatmap on Array with Elements Are Optional Has Different Behavior
Convert Input Data to Integer in Swift
Anchor Constraints Not Honored in Xcode 10/iOS 12
Does Realm Support Computed Property in Swift
Swift Bug? Calling Super Class Method When Subclass with Generic Type
Swift - Setting Calayer Bounds or Frame Not Working
<Unknown>:0: Error: Opening Import File for Module 'swift': Not a Directory
List Scroll Freeze on Catalyst Navigationview
How to Delay a for Loop in Swift Without Interrupting The Main Thread