UITextView with large `textContainerInset` scrolls upwards when selecting text

Whenever a UITextView has a large textContainerInset it starts scrolling upwards rapidly when selecting text. For my use case, I have a custom view that I display in the text view to show the comment you are writing a reply to. To accomplish this, I use the textContainerInset and set the top portion of that inset to a large value.

Here is a video showing the issue: https://streamable.com/9z57ok

I've also made a very simple sample project to demonstrate and here is the main view controller code: https://gist.github.com/rickharrison/9cf563bcb22130bfafc7d3b5d37c55f2

Is it possible to disable scrolling of the UITextView while the selection UI is shown? Or is there another way I should inset the text to edit?

I found another thread with this issue from years ago, but no solution - https://developer.apple.com/forums/thread/129882

Answered by Frameworks Engineer in 793635022

This appears to be a real bug in the way that UITextView calculates the intended scroll position for a given selection point. This bug will be addressed in a future software update (apologies, I am not allowed to provide timelines).

There are some workarounds that you can try, however. One thing I would suggest is to consider some alternatives for textContainerInset. If you need extra space at the top of your UITextView to place other content, for example, consider instead placing your text view inside of a container UIScrollView. You can set UITextView's isScrollEnabled to false, which will then allow you to obtain it's desired content size via intrinsicContentSize or sizeThatFits:, which would then allow you to lay out this text view inside of a parent UIScrollView, for which you can place at any arbitrary y offset.

This bug has been around for a long time. I first reported it to Apple back in 2016 (radar ID 25220931; updated #FB6001878 last year).

In my app, I make it so that you can have a large gap below the text you are writing, so that the text is not always at the bottom of the editor (like in Xcode). I was initially using a large bottom textContainerInset to achieve this, but this triggered the selection scrolling bug you describe.

My workaround is to leave the bottom textContainerInset alone and to instead increase the contentSize height in a UITextView subclass. (I add the extra height whenever the contentSize is set in the subclass override.)

This really only works for adding space below, though. To add space above, as you want, you'd need to mess with contentInset, but doing so causes the same selection scrolling problem as adjusting the textContainerInset so unfortunately gets you nowhere.

In your situation I think the only solution is to push the entire top of the text view down to make space for the comment, even though that might not be ideal, as presumably you wanted the text view visible and scrollable beneath the comment.

@KeithB Thanks for the info, I really appreciate it! I might be able to change my UI around so that the content could be below the text you are editing. Did you just override setContentSize: to update it to the size with the extra bottom margin?

I noticed in another app, they somehow removed all animations/scrolling. When moving the selection anchors the text view does not scroll at all. Also, when entering a new line, there is no animation to scroll the UITextView, it just jumps instantly. Do you know if there is a way to replicate that behavior?

Thanks!

This appears to be a real bug in the way that UITextView calculates the intended scroll position for a given selection point. This bug will be addressed in a future software update (apologies, I am not allowed to provide timelines).

There are some workarounds that you can try, however. One thing I would suggest is to consider some alternatives for textContainerInset. If you need extra space at the top of your UITextView to place other content, for example, consider instead placing your text view inside of a container UIScrollView. You can set UITextView's isScrollEnabled to false, which will then allow you to obtain it's desired content size via intrinsicContentSize or sizeThatFits:, which would then allow you to lay out this text view inside of a parent UIScrollView, for which you can place at any arbitrary y offset.

UITextView with large `textContainerInset` scrolls upwards when selecting text
 
 
Q