40

I am making a an app that has a UITextView and a button.

When I click the button some text will add in the UITextView.

But when clicking the button, I wan't to scroll down to the bottom of the text field so the user can see the last text added.

How to make the UITextView to scroll down to the bottom?

I tried:

int numLines = LogTextView.contentSize.height / LogTextView.font.lineHeight+1;
NSLog(@"%d",numLines);

NSUInteger length = self.LogTextView.text.length;
self.LogTextView.selectedRange = NSMakeRange(0, length);

but it will not work...

I also tried:

self.LogTextView.contentSize=CGSizeMake(length,0);
3
  • First of all is text view because a UITextView, because the text field doesn't have a scroll. ;) And maybe you could use scrollRangeToVisible method :) Commented May 22, 2013 at 18:02
  • 1
    as @danypata questioned, is this a uitextview, or a uitextfield in a uiscrollview? you referenced both text field and text view in the question. Commented May 22, 2013 at 18:08
  • @andrewlattis the question is about UITextView which is a sub of UIScrollView which has contentSize as a property. Commented Apr 19, 2016 at 19:36

10 Answers 10

87

You can use the following code if you are talking about UITextView:

-(void)scrollTextViewToBottom:(UITextView *)textView {
     if(textView.text.length > 0 ) {
        NSRange bottom = NSMakeRange(textView.text.length -1, 1);
        [textView scrollRangeToVisible:bottom];
     }

}

SWIFT 4:

func scrollTextViewToBottom(textView: UITextView) {
    if textView.text.count > 0 {
        let location = textView.text.count - 1
        let bottom = NSMakeRange(location, 1)
        textView.scrollRangeToVisible(bottom)
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

DON'T FORGET TO ADD [textView setScrollEnabled:NO]; [textView setScrollEnabled:YES];
@ofirmalachi, did you mean to write conflicting statements in your comment above?
@stuckj I think what he meant what disable scrolling when it is still scrolling down and the enable it again after it is finished. CMIIW.
Note using scrollRangeToVisible with large texts may make UITextView freeze because of animation. Better use contentOffset as shown here stackoverflow.com/a/42478029/3004003, but set animated to false
19

Try this if you have problem on iOS 7 or above. See this SO answer.

- (void)scrollTextViewToBottom:(UITextView *)textView {
    NSRange range = NSMakeRange(textView.text.length, 0);
    [textView scrollRangeToVisible:range];
    // an iOS bug, see https://stackoverflow.com/a/20989956/971070
    [textView setScrollEnabled:NO];
    [textView setScrollEnabled:YES];
}

1 Comment

IOS 13 and the bug is still here!
12

With Swift 3

let bottom = self.textView.contentSize.height - self.textView.bounds.size.height
self.textView.setContentOffset(CGPoint(x: 0, y: bottom), animated: true)

5 Comments

It should work. Where are you using this code? Make sure you're using it after textView has been loaded.
It's after viewDidAppear, and textview is fully loaded, with content. Might be related to the fact that the textview's content is larger than the screen's bounds by a large margin
I've just re-verified and it's working just fine. I've created a new single view project, drag and drop a textView with default size and default text, connected outlet, put this code inside viewDidAppear. And it correctly scrolled to bottom.
It's better to add check if bottom positive or not, because if it is negative then there is no need to change contentOffset.
Doesn't work for me (iOS11). My text spans many lines and uses a custom font.
7

Swift 5

extension UITextView {
    func simple_scrollToBottom() {
        let textCount: Int = text.count
        guard textCount >= 1 else { return }
        scrollRangeToVisible(NSRange(location: textCount - 1, length: 1))
    }
}

// Usage
textView.simple_scrollToBottom()

4 Comments

Excellent recommendation! Works really well. Placed the extension at bottom of code after class } and then used the //Usage. code in the button Action.
change this for swift 5 scrollRangeToVisible(NSRange(location: textCount - 1, length: 1))
Thanks @AMAN77 I have inserted your changes.
It is need to be called by delay after set text
5

Make a range, specifying encoding, to the last character, then scroll to that range Something other than utf8 might be appropriate depending on your content

let range = NSMakeRange(self.textView.text.lengthOfBytes(using: .utf8), 0);
self.textView.scrollRangeToVisible(range);

2 Comments

The best solution of all with the minimum lag!
this works on iOS 14.4 as well. smooth and no issues at all
4

You have to implement a delegate method. The code below checks whether a newline has been entered and, if so, scrolls to the bottom of the textView:

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{

    if ([text isEqualToString:@"\n"]) {
        textView.contentOffset = CGPointMake(0.0, textView.contentSize.height);
    }
    return YES;
}

Comments

3

This works for me! :D

CGPoint bottomOffset = CGPointMake(0, self.textView.contentSize.height - self.textView.bounds.size.height);
[self.description1 setContentOffset:bottomOffset animated:YES];

1 Comment

This works well for when you need to do this without animation (with animated:NO of course). Note that you might want to add in a max(0, ... to scroll down only when necessary.
3

As a generic approach for scrolling to bottom, it can be done on a UIScrollView.

extension UIScrollView {
    func scrollToBottom() {
        let contentHeight = contentSize.height - frame.size.height
        let contentoffsetY = max(contentHeight, 0)
        setContentOffset(CGPoint(x: 0, y: contentoffsetY), animated: true)
    }
}

This will work on all descendants of UIScrollView like UITextView, UITableView etc..

Comments

3
textView.scrollRangeToVisible(NSRange(..<textView.text.endIndex, in: textView.text))

This solution does a couple of notable things slightly different:

  • Utilizes the String.Index interface (likely more performant than e.g. .count)
  • Uses a PartialRangeUpTo which avoids an explicit range start position, reducing the code to a clean one-liner

Comments

1

The Swift version of @Hong Duan answer

func scrollTextViewToBottom(textView: UITextView) {
    if textView.text.count > 0 {
        let location = textView.text.count - 1
        let bottom = NSMakeRange(location, 1)
        textView.scrollRangeToVisible(bottom)

        // an iOS bug, see https://stackoverflow.com/a/20989956/971070
        textView.isScrollEnabled = false
        textView.isScrollEnabled = true
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.