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.
-
if the `textView` is scrollable you can scroll? What exactly you mean? Have you tried it before posting?Show some code please:) – Korpel Oct 30 '15 at 00:35
-
what version of os you are using? – PK20 Oct 30 '15 at 00:39
-
`self.textView.scrollRangeToVisible(NSMakeRange(0, 0))` try this where for range put the end of the `textView` – Korpel Oct 30 '15 at 00:40
-
Try this [link](http://stackoverflow.com/questions/19124037/scroll-to-bottom-of-uitextview-erratic-in-ios-7) – PK20 Oct 30 '15 at 00:40
-
@Korpel I edited my question. I want to scroll programmatically. Imagine a textView full of characters. I want to press a button and scroll to the bottom of this textView programmatically. You can see the Objective-C solution in the link above. – bar5um Oct 30 '15 at 00:43
-
@Barsam have a look at my second comment and see if that helps you out.`self.textView.scrollRangeToVisible(NSMakeRange(-1, 1))` – Korpel Oct 30 '15 at 00:44
-
@PK20 Xcode 7 , iOS 8.4 and Swift 2.0 – bar5um Oct 30 '15 at 00:45
-
good. Then the solution is at the bottom of the link I provided. Regards – PK20 Oct 30 '15 at 00:47
11 Answers
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))

- 520
- 1
- 4
- 13
-
Wow I was having a hard time with choppy scrolling and weird glyph cutoffs sometimes and turns out `.layoutManager.allowsNonContiguousLayout = false` is the silver bullet, thanks! – Chee-Yi May 02 '18 at 04:30
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!

- 7,009
- 1
- 58
- 44
-
Since Swift 4.x and in order to avoid a deprecated warning, use textLog.text.count rather than textLog.text.characters.count :-) – Antoine Rucquoy Dec 26 '17 at 10:02
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

- 1,986
- 2
- 19
- 41
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.

- 128,072
- 22
- 173
- 272
-
3So who downvoted my answer, and why? I'm trying to teach the OP how to solve these sorts of problems for himself rather than giving him the converted code. In the long run learning how to figure this out for yourself is a lot more helpful than having somebody else spoon-feed you finished code. – Duncan C Oct 30 '15 at 00:46
-
i totally agree with you Sir.Your answer is giving legit steps to help the OP.Upvoted – Korpel Oct 30 '15 at 00:47
-
I disagree, this is a very non-standard way to scroll a text view. Most iOS are most familiar with using `contentOffset`. Not to mention, `NSRange` will most likely be deprecated in the future in favor of Swift's `Range`. – barndog Oct 30 '15 at 00:54
-
I answered in terms of the code that the OP found in order to provide a lesson in how to translate Objective-C to Swift. I will bet a large sum of money that NSRange will not be deprecated in the next 2 or 3 years since it is widely used in Objective-C code, and Objective-C is going to be around for a very long time. – Duncan C Oct 30 '15 at 01:00
-
Your solution of using the textView's contentOffset property and `setContentOffset:animated:` is good, but I felt that teaching the OP how to understand Objective-C was important. – Duncan C Oct 30 '15 at 01:02
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.

- 3,507
- 2
- 21
- 26
Language:Swift
Follow steps as below:
//Declare
@IBOutlet weak var trTextDataRead: UITextView!
//Cunstom method
func insertTextView(text: String){
//Insert text
trTextDataRead.text.append(text)
//Scroll to the end
let btm = NSMakeRange(trTextDataRead.text.lengthOfBytes(using: String.Encoding.utf8), 0)
trTextDataRead.scrollRangeToVisible(btm)
}

- 54
- 4
If you're dealing with the UITextView's attributedText property:
in viewDidLoad()
self.storyTextView.layoutManager.allowsNonContiguousLayout = false
in your scrolling method
let stringLength:Int = self.storyTextView.attributedText.string.characters.count
self.storyTextView.scrollRangeToVisible(NSMakeRange(stringLength-1, 0))

- 1,463
- 1
- 13
- 13
Swift 4
private func textViewScrollToBottom() {
let bottomRange = NSMakeRange(self.myTextView.text.count - 1, 1)
self.myTextView.scrollRangeToVisible(bottomRange)
}

- 128
- 1
- 10
UITextView
has a property contentOffsent
. You can either set textView.contentOffset
or textView.setContentOffset(offset, animated: true)
For example if the contentSize
of your text view is (100, 500)
but the height of the text view is only 100, then to scroll to the bottom, set the contentOffset
property to (0, 400)
(this is for a vertical text view). More generically the formula for scrolling to the bottom is textView.contentSize.height-textView.height
. Every time your button is pressed, set the offset.
I would also really recommend reading the documentation and trying to figure it out. Swift and iOS is quite well documented and a question like this is easily searchable via Google.
Edit: This works because UITextView
inherits from UIScrollView
.
Sidenote: I wrote a UITextView
subclass where you can set the vertical text alignment so if you set the text alignment to .Bottom
, the text will align with the bottom of the view.
class TextView: UITextView {
enum VerticalAlignment: Int {
case Top = 0, Middle, Bottom
}
var verticalAlignment: VerticalAlignment = .Middle
//override contentSize property and observe using didSet
override var contentSize: CGSize {
didSet {
let textView = self
let height = textView.bounds.size.height
let contentHeight:CGFloat = contentSize.height
var topCorrect: CGFloat = 0.0
switch(self.verticalAlignment){
case .Top:
textView.contentOffset = CGPointZero //set content offset to top
case .Middle:
topCorrect = (height - contentHeight * textView.zoomScale)/2.0
topCorrect = topCorrect < 0 ? 0 : topCorrect
textView.contentOffset = CGPoint(x: 0, y: -topCorrect)
case .Bottom:
topCorrect = textView.bounds.size.height - contentHeight
topCorrect = topCorrect < 0 ? 0 : topCorrect
textView.contentOffset = CGPoint(x: 0, y: -topCorrect)
}
if contentHeight >= height { //if the contentSize is greater than the height
topCorrect = contentHeight - height //set the contentOffset to be the
topCorrect = topCorrect < 0 ? 0 : topCorrect //contentHeight - height of textView
textView.contentOffset = CGPoint(x: 0, y: topCorrect)
}
}
}
// MARK: - UIView
override func layoutSubviews() {
super.layoutSubviews()
let size = self.contentSize //forces didSet to be called
self.contentSize = size
}
}
In the above example (pulled directly from my subclass), you'll notice I make extensive use of the contentOffset
property. I do some calculations to figure out where the offset should be based on the vertical alignment property and then set the content offset property according (which is how you programmatically scroll with a scroll view)

- 6,975
- 8
- 53
- 105
A lot of people are explaining how to scroll to the bottom, but one thing to note is that this won't work if you place it in viewDidLoad
. For example: I needed to use this to scroll a log to the bottom when the page loaded. In order to do this, I had to implement the following code
- (void) viewDidLoad {
[super viewDidLoad];
[_logTextView setText:[Logger loadLogText]];
}
- (void) viewDidLayoutSubviews {
[_logTextView
setContentOffset:CGPointMake(
0.0,
_logTextView.contentSize.height
- _logTextView.frame.size.height
)
animated:NO
];
}
The actual scrolling of the UITextView cannot be done in viewDidLoad
.
In my viewDidLoad
implementation I set the text for the text box.
In my viewDidLayoutSubviews
implementation I set the content offset for the UITextView by generating a CGPoint using the height of the text views content minus the height of the text view itself. This way, when it scrolls to the bottom, the bottom of the text is not at the top of the box and instead, the bottom of the text is at the bottom of the box.

- 3,250
- 3
- 35
- 69
Swift 5 - with extension to UITextView + avoid slow scroll in large texts (this issue killed my main Thread)
extension UITextView {
func scrollToBottom() {
// IMPORTANT - only use (text as NSString) to get the length, since text.length is O(N) and it will kill the main thread.
let length = (text as NSString).length
if length > 1 {
scrollRangeToVisible(NSMakeRange(length - 1, 1))
}
}
}

- 83
- 12