在 SwiftUI 中突出显示语音

Higlight speech utterance in SwiftUI

我无法在 SwiftUI 中突出显示文本话语。我只在 UIKit 中找到过它的例子。 在 UIKit 中它应该是 var label: UILabel!,但在 SwiftUI 中标签必须是字符串。我试图在函数内部将 NSMutableAttributedString 转换为 String 格式,但它在抱怨。 如何用 String 格式解决它,以便它也能在 SwiftUI 中工作?

import AVFoundation

class Speaker: NSObject {
    let synth = AVSpeechSynthesizer()
    var label: String // <- problem
    

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

    func speak(_ string: String) {
        let utterance = AVSpeechUtterance(string: string)
        utterance.voice = AVSpeechSynthesisVoice(language: "en-GB")
        utterance.rate = 0.4
        synth.speak(utterance)
    }
    
    // Functions to highlight text
    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, willSpeakRangeOfSpeechString characterRange: NSRange, utterance: AVSpeechUtterance) {
        let mutableAttributedString = NSMutableAttributedString(string: utterance.speechString)
        mutableAttributedString.addAttribute(.foregroundColor, value: UIColor.red, range: characterRange)
        label.attributedText = mutableAttributedString
    }

    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
        label.attributedText = NSAttributedString(string: utterance.speechString)
    }
}

我建议您将 UILabel 包裹在 UIViewRepresentable 中,这样您就可以像以前一样继续使用属性字符串:


struct ContentView : View {
    @ObservedObject var speaker = Speaker()
    
    var body: some View {
        VStack {
            LabelRepresented(text: speaker.label)
        }.onAppear {
            speaker.speak("Hi. This is a test.")
        }
    }
}

struct LabelRepresented: UIViewRepresentable {
    var text : NSAttributedString?
    
    func makeUIView(context: Context) -> UILabel {
        return UILabel()
    }
    
    func updateUIView(_ uiView: UILabel, context: Context) {
        uiView.attributedText = text
    }
}

class Speaker: NSObject, ObservableObject, AVSpeechSynthesizerDelegate {
    let synth = AVSpeechSynthesizer()
    @Published var label: NSAttributedString? // <- change to AttributedString
    

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

    func speak(_ string: String) {
        let utterance = AVSpeechUtterance(string: string)
        utterance.voice = AVSpeechSynthesisVoice(language: "en-GB")
        utterance.rate = 0.4
        synth.speak(utterance)
    }
    
    // Functions to highlight text
    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, willSpeakRangeOfSpeechString characterRange: NSRange, utterance: AVSpeechUtterance) {
        let mutableAttributedString = NSMutableAttributedString(string: utterance.speechString)
        mutableAttributedString.addAttribute(.foregroundColor, value: UIColor.red, range: characterRange)
        label = mutableAttributedString
    }

    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
        label = NSAttributedString(string: utterance.speechString)
    }
}

我将 label 更改为 NSAttributedString? 并使其成为 ObservableObject 上的 @Published 属性 —— 这样,当它发生变化时,视图会得到通知马上。

UIViewRepresentable 创建一个标签,并在其更改时使用属性字符串对其进行更新。

如果您确实想尝试更纯粹的 SwiftUI 方法,这篇博文提供了一些在 SwiftUI 中使用 NSAttributedString 的好资源(包括我采用的方法): https://swiftui-lab.com/attributed-strings-with-swiftui/