2

I've been trying to figure out a way to set the cursor to the bottom of a long UITextView with animation. Setting it wasn't too difficult, in fact this answer covers it very well. https://stackoverflow.com/a/34922332/563381

However, animating it isn't so easy. There isn't a setSelectedTextRange(animated:) that I can find. Setting it inside a UIView.animate... block doesn't seem to do anything. The best I've been able to come up with is to animate the scroll manually then in the completion set the selectedTextRange. However this is fragile, often looks choppy, and seems on occasion to not work at all.

When you set selectedTextRange it does jump to that location. If there was a way to avoid that jump the animation might be smoother and at least would be less fragile since it wouldn't require a delay and you could use setContentOffset(animated) without needing to wait to set the selectedTextRange.

The other option is to find a way to cause the selectedTextRange jump to be animated itself. On this front I tried the trick of disabling scrolling before and reenabling after but that didn't seem to work for me. Guessing that has changed in later versions of iOS.

Ryan Poolos
  • 18,421
  • 4
  • 65
  • 98

1 Answers1

1

You can use setContentOffset(_, animated:) and detect the end of the animation with scrollViewDidEndScrollingAnimation and set the cursor like so:

// The action to scroll down
@IBAction func test(_ sender: Any) {

    let offset = self.textView.contentSize.height - self.textView.frame.height
    self.textView.setContentOffset(CGPoint(x: 0, y: offset), animated: true)
}

// Setting the cursor down
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {

    let offset = self.textView.contentSize.height - self.textView.frame.height

    if scrollView.contentOffset == CGPoint(x: 0, y: offset) {
        let newPosition = textView.endOfDocument
        self.textView.selectedTextRange = self.textView.textRange(from: newPosition, to: newPosition)
    }
}

You will need to add UITextViewDelegate to your view controller and set the textView delegate:

textView.delegate = self
Casper Zandbergen
  • 3,419
  • 2
  • 25
  • 49
  • Using `scrollViewDidEndScrollingAnimation(scrollView:)` is a little better than waiting for a completion block because I don't have to worry about how long the animation is. But I'd love something cleaner that wouldn't leave the cursor at the top while this was animating. – Ryan Poolos Jul 20 '17 at 21:57
  • I don't see how it matter that the cursor appears after the animation rather than during the animation. You dont want your user typing while the animation is going I think? So you should actually disable the cursor while the animation is going. @RyanPoolos – Casper Zandbergen Jul 20 '17 at 22:11
  • @RyanPoolos If this helped you please accept the answer – Casper Zandbergen Jul 25 '17 at 12:59