iosswiftuiscrollviewuilabelsfspeechrecognizer

Programmatically scroll uiscrollview to bottom as uilabel grows in height


I am using SFSpeechRecognizer to transcribe audio to text. As the audio is transcribed, I am printing it out as results come out to a uilabel, which is a subview of a uiscrollview. The uilabel is pinned to the top and bottom of the uiscrollview.

My problem is programmatically scrolling the uiscrollview to the bottom as the uilabel grows in height from the transcription.

I tried solutions such as this but it does not work. Any guidance would be much appreciated.

Code I've tried:

SFSpeechRecognizer().recognitionTask(with: request) { [unowned self] (result, error) in

                        guard let result = result else {
                            
                            DispatchQueue.main.async {
                                self.transcriptionLabel.text = "We're not able to transcribe."
                            }
                            
                            return
                        }
                            
                        DispatchQueue.main.async {
                            self.transcriptionLabel.text = result.bestTranscription.formattedString
                            
                            if self.transcriptionScrollView.contentSize.height - self.transcriptionScrollView.bounds.size.height > 0 {
                                let bottomOffset = CGPoint(x: 0, y: self.transcriptionScrollView.contentSize.height - self.transcriptionScrollView.bounds.height + self.transcriptionScrollView.contentInset.bottom)
                                self.transcriptionScrollView.setContentOffset(bottomOffset, animated: true)
                            }
                            self.view.layoutIfNeeded()
                        }
                    }

Solution

  • Again, the first thing you need to do is layout your subviews after updating the text so your computations are based on the new text.

    Rather than jumping through a lot of hoops and trying to compute the contentOffset I would suggest using scrollToRectVisible(_:animated:).

        view.layoutIfNeeded()
    
        let rect = CGRect(
            x: 0,
            y: transcriptionLabel.frame.maxY - 1,
            width: 1,
            height: 1)
    
        transcriptionScrollView.scrollRectToVisible(rect, animated: true)
    

    That should scroll you to the bottom of the label, assuming that your label is a subview of your scrollView. If not, you will need to convert the label's frame to the scrollView coordinate space.

    Remember to call view.layoutIfNeeded() after updating the text in your label and before scrolling.