Support
Quality
Security
License
Reuse
kandi has reviewed Utterance and discovered the below as its top functions. This is intended to give you an instant insight into Utterance implemented functionality, and help decide if they suit your requirements.
Get all kandi verified functions for this library.
Get all kandi verified functions for this library.
Utterance lets you use the platform's native Text To Speech Engine within your Titanium Project
See all related Code Snippets
QUESTION
How to update view from callback inside of custom delegate class?
Asked 2022-Apr-03 at 12:15I am working on a Christian app, all is going well, except for 1 thing: I can't solve how to get the label to update its text after my AVSpeechSynthesizer has finished speaking.
For example, after the prayer has finished being read, the text should update to "Play" again. It does this correctly in all other known scenarios (Pause works, Resume works, stop works, restart works, etc. as in the label updates accordingly).
Please see my code here:
import SwiftUI
import AVFoundation
class GlobalVarsModel: ObservableObject {
@Published var prayerAudioID: UUID?
@Published var uttPrayerAudio = ""
@Published var strAudioBtnImgStr = "play.fill"
@Published var strAudioBtnText = "Play Audio"
static let audioSession = AVAudioSession.sharedInstance()
static var synthesizer = CustomAVSpeechSynth()
}
class CustomAVSpeechSynth: AVSpeechSynthesizer, AVSpeechSynthesizerDelegate {
//NOT DESIRED OUTPUT LIST
//@Published
//@ObservedObject
//@State
@StateObject var gVars = GlobalVarsModel()
override init() {
super.init()
delegate = self
}
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didPause utterance: AVSpeechUtterance) {
}
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didStart utterance: AVSpeechUtterance) {
}
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didCancel utterance: AVSpeechUtterance) {
}
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
print("Finished praying.")
print(gVars.strAudioBtnText)
}
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didContinue utterance: AVSpeechUtterance) {
}
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, willSpeakRangeOfSpeechString characterRange: NSRange, utterance: AVSpeechUtterance) {
}
}
struct TappedPrayerView: View {
public var tappedPrayer: Prayer
@StateObject public var gVars = GlobalVarsModel()
@Environment(\.scenePhase) var scenePhase
var body: some View {
ScrollView {
VStack {
Text(tappedPrayer.strTitle).font(.title2).padding()
HStack {
Spacer()
Button {
gVars.prayerAudioID = tappedPrayer.id
gVars.uttPrayerAudio = tappedPrayer.strText
if (gVars.strAudioBtnText == "Play Audio") {
gVars.strAudioBtnImgStr = "pause.fill"
gVars.strAudioBtnText = "Pause Audio"
if (GlobalVarsModel.synthesizer.isSpeaking || GlobalVarsModel.synthesizer.isPaused) {
GlobalVarsModel.synthesizer.stopSpeaking(at: .immediate)
GlobalVarsModel.synthesizer.speak(AVSpeechUtterance(string: gVars.uttPrayerAudio))
} else {
GlobalVarsModel.synthesizer.speak(AVSpeechUtterance(string: gVars.uttPrayerAudio))
}
} else if (gVars.strAudioBtnText == "Pause Audio") {
GlobalVarsModel.synthesizer.pauseSpeaking(at: .immediate)
gVars.strAudioBtnImgStr = "play.fill"
gVars.strAudioBtnText = "Continue Audio"
} else if (gVars.strAudioBtnText == "Continue Audio") {
if (GlobalVarsModel.synthesizer.isPaused) {
GlobalVarsModel.synthesizer.continueSpeaking()
gVars.strAudioBtnImgStr = "pause.fill"
gVars.strAudioBtnText = "Pause Audio"
}
}
} label: {
Label(gVars.strAudioBtnText, systemImage: gVars.strAudioBtnImgStr).font(.title3).padding()
}.onAppear {
if ((GlobalVarsModel.synthesizer.isSpeaking || GlobalVarsModel.synthesizer.isPaused) && tappedPrayer.id != gVars.prayerAudioID) {
gVars.strAudioBtnImgStr = "play.fill"
gVars.strAudioBtnText = "Play Audio"
}
}
Spacer()
Button {
if (GlobalVarsModel.synthesizer.isSpeaking || GlobalVarsModel.synthesizer.isPaused) {
GlobalVarsModel.synthesizer.stopSpeaking(at: .immediate)
gVars.strAudioBtnImgStr = "play.fill"
gVars.strAudioBtnText = "Play Audio"
gVars.prayerAudioID = UUID(uuidString: String(Int.random(in: 0..<7)) + (gVars.prayerAudioID?.uuidString ?? "777"))
}
} label: {
Label("Restart", systemImage: "restart.circle.fill").font(.title3).padding()
}
Spacer()
}
Spacer()
Text(tappedPrayer.strText).padding()
Spacer()
}
}.onAppear {
if (GlobalVarsModel.synthesizer.isPaused) {
if (tappedPrayer.id == gVars.prayerAudioID) {
gVars.strAudioBtnImgStr = "play.fill"
gVars.strAudioBtnText = "Continue Audio"
}
} else if (GlobalVarsModel.synthesizer.isSpeaking) {
if (tappedPrayer.id == gVars.prayerAudioID) {
gVars.strAudioBtnImgStr = "pause.fill"
gVars.strAudioBtnText = "Pause Audio"
}
} else {
gVars.strAudioBtnImgStr = "play.fill"
gVars.strAudioBtnText = "Play Audio"
}
}.onChange(of: scenePhase) { newPhase in
if (newPhase == .active) {
} else if (newPhase == .inactive) {
} else if (newPhase == .background) {
}
}
}
struct TappedPrayerView_Previews: PreviewProvider {
static var previews: some View {
let defaultPrayer = Prayer(strTitle: "Default title", strText: "Default text")
TappedPrayerView(tappedPrayer: defaultPrayer)
}
}
}
ANSWER
Answered 2022-Apr-03 at 12:15Multiple issues with your code.
You are initializing GlobalVarsModel
twice. Once in the View and once in the delegate. So changes in one won´t reflect in the other.
You are implementing the delegate in a subclass of your AVSpeechSynthesizer
therefor it is capsulated in it and you can´t update your View when an event arises.
I changed the implementation to address this issues:
class GlobalVarsViewmodel: NSObject, ObservableObject { //You need to derive from NSObject first, because `AVSpeechSynthesizer` is `objc` related
@Published var prayerAudioID: UUID?
@Published var uttPrayerAudio = ""
@Published var strAudioBtnImgStr = "play.fill"
@Published var strAudioBtnText = "Play Audio"
let audioSession = AVAudioSession.sharedInstance()
var synthesizer = CustomAVSpeechSynth()
override init(){
super.init()
synthesizer.delegate = self // assign the delegate
}
}
extension GlobalVarsViewmodel: AVSpeechSynthesizerDelegate{ // extend the viewmodel to implement the delegate
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didPause utterance: AVSpeechUtterance) {
}
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didStart utterance: AVSpeechUtterance) {
}
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didCancel utterance: AVSpeechUtterance) {
}
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
print("Finished praying.")
strAudioBtnImgStr = "play.fill" // here assign the text and button appearance
strAudioBtnText = "Play Audio"
}
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didContinue utterance: AVSpeechUtterance) {
}
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, willSpeakRangeOfSpeechString characterRange: NSRange, utterance: AVSpeechUtterance) {
}
}
// I don´t think you need this anymore
class CustomAVSpeechSynth: AVSpeechSynthesizer {
//NOT DESIRED OUTPUT LIST
//@Published
//@ObservedObject
//@State
}
struct TappedPrayerView: View {
var tappedPrayer: Prayer
@StateObject private var gVars = GlobalVarsViewmodel()
@Environment(\.scenePhase) var scenePhase
var body: some View {
ScrollView {
VStack {
Text(tappedPrayer.strTitle).font(.title2).padding()
HStack {
Spacer()
Button {
gVars.prayerAudioID = tappedPrayer.id
gVars.uttPrayerAudio = tappedPrayer.strText
if (gVars.strAudioBtnText == "Play Audio") {
gVars.strAudioBtnImgStr = "pause.fill"
gVars.strAudioBtnText = "Pause Audio"
if (gVars.synthesizer.isSpeaking || gVars.synthesizer.isPaused) {
gVars.synthesizer.stopSpeaking(at: .immediate)
gVars.synthesizer.speak(AVSpeechUtterance(string: gVars.uttPrayerAudio))
} else {
gVars.synthesizer.speak(AVSpeechUtterance(string: gVars.uttPrayerAudio))
}
} else if (gVars.strAudioBtnText == "Pause Audio") {
gVars.synthesizer.pauseSpeaking(at: .immediate)
gVars.strAudioBtnImgStr = "play.fill"
gVars.strAudioBtnText = "Continue Audio"
} else if (gVars.strAudioBtnText == "Continue Audio") {
if (gVars.synthesizer.isPaused) {
gVars.synthesizer.continueSpeaking()
gVars.strAudioBtnImgStr = "pause.fill"
gVars.strAudioBtnText = "Pause Audio"
}
}
} label: {
Label(gVars.strAudioBtnText, systemImage: gVars.strAudioBtnImgStr).font(.title3).padding()
}.onAppear {
if ((gVars.synthesizer.isSpeaking || gVars.synthesizer.isPaused) && tappedPrayer.id != gVars.prayerAudioID) {
gVars.strAudioBtnImgStr = "play.fill"
gVars.strAudioBtnText = "Play Audio"
}
}
Spacer()
Button {
if (gVars.synthesizer.isSpeaking || gVars.synthesizer.isPaused) {
gVars.synthesizer.stopSpeaking(at: .immediate)
gVars.strAudioBtnImgStr = "play.fill"
gVars.strAudioBtnText = "Play Audio"
gVars.prayerAudioID = UUID(uuidString: String(Int.random(in: 0..<7)) + (gVars.prayerAudioID?.uuidString ?? "777"))
}
} label: {
Label("Restart", systemImage: "restart.circle.fill").font(.title3).padding()
}
Spacer()
}
Spacer()
Text(tappedPrayer.strText).padding()
Spacer()
}
}.onAppear {
if (gVars.synthesizer.isPaused) {
if (tappedPrayer.id == gVars.prayerAudioID) {
gVars.strAudioBtnImgStr = "play.fill"
gVars.strAudioBtnText = "Continue Audio"
}
} else if (gVars.synthesizer.isSpeaking) {
if (tappedPrayer.id == gVars.prayerAudioID) {
gVars.strAudioBtnImgStr = "pause.fill"
gVars.strAudioBtnText = "Pause Audio"
}
} else {
gVars.strAudioBtnImgStr = "play.fill"
gVars.strAudioBtnText = "Play Audio"
}
}.onChange(of: scenePhase) { newPhase in
if (newPhase == .active) {
} else if (newPhase == .inactive) {
} else if (newPhase == .background) {
}
}
}
struct TappedPrayerView_Previews: PreviewProvider {
static var previews: some View {
let defaultPrayer = Prayer(strTitle: "Default title", strText: "Default text")
TappedPrayerView(tappedPrayer: defaultPrayer)
}
}
}
Remarks:
GlobalVarsModel
to GlobalVarsViewmodel
because it is exactly that, a Viewmodel.AVAudioSession
Edit to adress the comment for clarification: I changed the implementation from static because it is not needed here. You can read more about it here -> https://www.donnywals.com/effectively-using-static-and-class-methods-and-properties/
Community Discussions, Code Snippets contain sources that include Stack Exchange Network
No vulnerabilities reported
Find more information at:
Save this library and start creating your kit
See Similar Libraries in
Save this library and start creating your kit
Open Weaver – Develop Applications Faster with Open Source