I made a button that adds some text to a textView and I want it to automatically scroll to the bottom as it is pressed so that user would be able to see the new text added.
I can't use this solution in Swift because I don't know Objective-C.
Does anyone know how can I scroll to the bottom of a textView in Swift? Thanks.
Tried both content offset and scrolltoview solutions, get mixed results and choppy scrolling; having looked around the below seemed to work and produce consistent scrolling to the bottom when needed.
In viewdidload:
self.storyTextView.layoutManager.allowsNonContiguousLayout = false
Then when needed:
let stringLength:Int = self.storyTextView.text.characters.count
self.storyTextView.scrollRangeToVisible(NSMakeRange(stringLength-1, 0))
Swift 4
let bottom = NSMakeRange(textLog.text.count - 1, 1)
textLog.scrollRangeToVisible(bottom)
Swift 3
let bottom = NSMakeRange(textLog.text.characters.count - 1, 1)
textLog.scrollRangeToVisible(bottom)
Update: thanks @AntoineRucquoy for Swift 4 reminder!
Simply, where myTextView
is the UITextView
in question:
let bottom = myTextView.contentSize.height
myTextView.setContentOffset(CGPoint(x: 0, y: bottom), animated: true) // Scrolls to end
So if you click the link you posted the accepted answer shows this objective-C code:
-(void)scrollTextViewToBottom:(UITextView *)textView
{
if(textView.text.length > 0 )
{
NSRange bottom = NSMakeRange(textView.text.length -1, 1);
[textView scrollRangeToVisible:bottom];
}
}
So your challenge is to convert that code to Swift.
Break it into pieces and tackle them one at a time. First, the method definition itself.
The method is called scrollTextViewToBottom. It takes a UITextView as a parameter, and does not return a result. How would you write that method definition in Swift?
Next look that the body of the method. The if statement should be exactly the same in Swift. The creation of an NSRange is all but identical. You just need to change it a little bit:
let bottom = NSMakeRange(textView.text.length -1, 1)
The part that's probably the hardest for somebody who doesn't know Objective-C is the method call. It's sending the message scrollRangeToVisible to the object textView
. The parameter passed is bottom
. See if you can rewrite that line in Swift. Then put the whole thing together.
I use the following in an app that scrolls to the bottom automatically when text is added:
First when initializing your textView, do the following:
textView.layoutManager.allowsNonContiguousLayout = false
textView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil)
Then add the following observer method:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
var bottom = textView.contentSize.height - textView.frame.size.height
if bottom < 0 {
bottom = 0
}
if textView.contentOffset.y != bottom {
textView.setContentOffset(CGPoint(x: 0, y: bottom), animated: true)
}
}
setting allowsNonContiguousLayout to false fixed contentSize problems for me.
Adding the contentSize observer will observe for any new changes in the contentSize of the textView and call the -observeValue(forKeyPath...) function when changes are made.
In the -observeValue(...) function, we first get the bottom (y contentOffset when fully scrolled to the bottom). We then check if that value is negative, meaning that the contentSize height is smaller than the textView frame height and you can't really do any scrolling. If you try to programmatically scroll with that negative value, it will cause that infamous jitter that many people know and love. So to avoid this jitter we simply set the value to what it already should be, 0 or you can also just return.
Then we just test to see if the contentOffset doesn't already equal the bottom value, we give it that new value. This avoids setting the contentOffset when it doesn't need to be set.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With