首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何从自定义委托类中的回调更新视图?

如何从自定义委托类中的回调更新视图?
EN

Stack Overflow用户
提问于 2022-04-02 08:16:21
回答 1查看 90关注 0票数 1

我正在开发一个基督教应用程序,一切都进行得很顺利,除了一件事:我无法解决如何在我的AVSpeechSynthesizer发言结束后得到标签来更新它的文本。

例如,祈祷完成后,文本应该更新为“播放”再次。它在所有其他已知的场景(暂停工作、恢复工作、停止工作、重新启动工作等)中正确地这样做,就像标签相应地更新一样。

请看我这里的代码:

代码语言:javascript
复制
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)
        }
    }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-04-02 10:23:28

您的代码有多个问题。

  1. 您正在初始化GlobalVarsModel两次。一次在视图中,一次在委托中。因此,其中一个的变化不会在另一个中反映。

  1. 您是在AVSpeechSynthesizer的子类中实现委托的,因此它被封装在其中,当事件出现时,您无法更新视图。

为了解决这一问题,我更改了实现:

代码语言:javascript
复制
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)
        }
    }
}

备注:

  • I将GlobalVarsModel的名称更改为GlobalVarsViewmodel,因为这正是视图模型。

编辑以说明注释:我将实现从静态更改为静态,因为这里不需要它。您可以在这里阅读更多关于-> https://www.donnywals.com/effectively-using-static-and-class-methods-and-properties/的内容。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71715901

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档