I used Apple's Text-to-Speech to speak text in a UITextView. It is created with SwiftUI but 'UITextView' is used From UIKit because I need the word highlight feature which SwiftUI cannot offer.
The problem is if it is a long text which cross the limit of UITextView, when TTS speaking word is beyond the last line, UITextView doesn't scroll down automatically to show the highlight word. Please refer the the video in below.
I think I should provide speaking word range in updateUIView function, but not sure how to do it. BTW, voiceManager.attributedText is passed from AVSpeechSynthesizerDelegate functions for highlight speaking word.
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
}
