Best Option for Programmatically Scrolling Long Dynamic Text

Hello!

I am trying to create an iOS app that is based around a very large, vertically scrolling text view. The text is broken up into many sections, and the user should be able to press buttons in the navigation, which programmatically scroll to those sections. The user can also change the font size in a settings menu. It should generally keep the user's spot when resizing fonts or rotating the screen (from portrait to landscape).

The problem I've been having is that no method of lazy text loading allows accurate enough navigation, and the text is too long to calculate the whole UI all at once. Here's my process in trying to find a solution:

My app is built in SwiftUI, so I started with a ScrollView and a LazyVStack, and I used .scrollPosition() and bound it to an Int?. It worked pretty well for most scroll locations both on screen and far off the screen, but when I programmatically scroll to a location that is off the screen but not very far off, it completely misses.

So, I investigated UIKit, and found that UITextView was a much better fit for the way I wanted to present the long text. I could also programmatically navigate by storing the NSRange of each section.

I tried to use scrollRangeToVisible(), but for long distance it would scroll so that the desired section was just below the viewport and thus off screen. Then I tried to use UITextView's textLayoutManager.textViewportLayoutController.relocateViewport() to send it to the correct NSTextRange, it would not jump all the way, but instead would do nothing until I tried to scroll again and it would jump slightly forward. I tried to use textViewportLayoutController.layoutViewport() after the jump, and that fixed the glitch when scrolling, but it still did not jump to the correct place, only slightly forward.

Then, I looked into TextKit 2 and the way it worked to try to find a solution. From what I can tell, it seems that to affect the NSTextViewportLayoutController without having to rewrite it, I need an NSTextViewportLayoutControllerDelegate, but the delegate required me to manually lay out the views, and in all the examples I've seen that use a custom NSTextViewportLayoutControllerDelegate, they wrote their own custom text view instead of using the default UITextView.

I started looking into writing a custom text view so I can get the programmatic scroll to work consistently. However, it felt like, from a maintainability standpoint, it would probably be best to stick with what Apple has already implemented.

For now, I found a workaround that scrolls consistently. Here is the code:

if let start = self.textView.position(from: self.textView.beginningOfDocument, offset: desiredLineRange.location) {
    let location = textView.caretRect(for: start)
    self.textView.setContentOffset(CGPoint(x: 0, y: location.origin.y), animated: false)
}
if let start = self.textView.position(from: self.textView.beginningOfDocument, offset: desiredLineRange.location) {
    let location = textView.caretRect(for: start)
    self.textView.setContentOffset(CGPoint(x: 0, y: location.origin.y), animated: false)
}

It does the job, because the first time it gets close enough, and the second time it gets to the precise location, but it just feels like a bit of a hack to run the same code twice. I was wondering if anyone knows what I could be doing wrong and if Apple provides any solutions for this?

(Also, all my UIKit navigation attempts ran inside an @objc func, which I passed using button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside). Just so you know in case it may be a problem with the way Swift and UIKit handle concurrency/parallel tasks).

Thank you!

Best Option for Programmatically Scrolling Long Dynamic Text
 
 
Q