沿着文本到语音朗读突出显示单词更改滚动文本视图

问题描述 投票:0回答:1

我使用 Apple 的文本转语音功能在 UITextView 中朗读文本。它是用 SwiftUI 创建的,但使用了 UIKit 中的“UITextView”,因为我需要 SwiftUI 无法提供的单词突出显示功能。

问题是,如果是超过 UITextView 限制的长文本,当 TTS 朗读单词超出最后一行时,UITextView 不会自动向下滚动以显示突出显示的单词。请参考下面的视频。

我认为我应该在

updateUIView
函数中提供口语单词范围,但不知道该怎么做。顺便说一句,
voiceManager.attributedText
是从 AVSpeechSynthesizerDelegate 函数传递的,用于突出显示说话的单词。

func makeUIView(context: Context) -> UITextView {
    let textView = UITextView()
    textView.delegate = context.coordinator
    
    textView.font = UIFont(name: "Times New Roman", size: 18)
    textView.textContainerInset = .init(top: 10, left: 10, bottom: 10, right: 10)
    textView.isScrollEnabled = true
    textView.isEditable = true
    textView.isUserInteractionEnabled = true
    textView.backgroundColor = .lightGreen20
    
    return textView
}

func updateUIView(_ uiView: UITextView, context: Context) {
    if !voiceManager.isSpeaking {
        let attStr = NSMutableAttributedString(string: textViewModel.text)
        let pStyle = NSMutableParagraphStyle()
        pStyle.lineSpacing = 3
        attStr.addAttribute(.paragraphStyle, value: pStyle, range: NSMakeRange(0, attStr.length))
        attStr.addAttribute(.font, value: UIFont(name: "TimesNewRomanPS-BoldMT", size: 18)!, range: NSMakeRange(0, attStr.length))
        
        uiView.attributedText = attStr
    } else {
        let attStr = NSMutableAttributedString(attributedString: voiceManager.attributedText!)
        let pStyle = NSMutableParagraphStyle()
        pStyle.lineSpacing = 3
        attStr.addAttribute(.paragraphStyle, value: pStyle, range: NSMakeRange(0, attStr.length))
        attStr.addAttribute(.font, value: UIFont(name: "TimesNewRomanPS-BoldMT", size: 18)!, range: NSMakeRange(0, attStr.length))
        
        uiView.attributedText = attStr
    }
}

// MARK: Delegate functions to highlight text
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, willSpeakRangeOfSpeechString characterRange: NSRange, utterance: AVSpeechUtterance) {
    let mutableAttributedString = NSMutableAttributedString(string: utterance.speechString)
    mutableAttributedString.addAttribute(.backgroundColor, value: UIColor.yellow, range: characterRange)
    
    attributedText = mutableAttributedString
}

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

swift swiftui uitextview text-to-speech avspeechsynthesizer
1个回答
0
投票

嗯,今天我脑子里冒出了一个想法。我设法实现了它。

            // Scroll only if text exceed textView border
            if uiView.contentSize.height > uiView.frame.size.height {
                // Scroll textView per speaking word change
                let location = voiceManager.characterRange?.location ?? 0
                let yOffset = round(Double(location) / Double(textViewModel.text.count) * uiView.frame.size.height)
                uiView.setContentOffset(.init(x: 0, y: yOffset), animated: true)
            }

一般来说,在

updateUIView(:)
下使用
characterRange / text.length
来获取文本中说出的单词的比例。然后将其与 uitextview 的高度相乘即可得到 Y 轴上的滚动偏移量。最后,将其输入到
setContentOffset(:)
函数中。它在我这边效果很好,但我想对于很长的文本来说可能不准确。以后会有更多测试。

© www.soinside.com 2019 - 2024. All rights reserved.