36

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);
Dan Rosenstark
  • 68,471
  • 58
  • 283
  • 421
Jonis
  • 415
  • 2
  • 6
  • 7
  • 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 :) – danypata May 22 '13 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. – andrew lattis May 22 '13 at 18:08
  • @andrewlattis the question is about `UITextView` which is a sub of `UIScrollView` which has `contentSize` as a property. – Dan Rosenstark Apr 19 '16 at 19:36

10 Answers10

84

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)
    }
}
andym
  • 67
  • 6
danypata
  • 9,895
  • 1
  • 31
  • 44
  • 2
    DON'T FORGET TO ADD [textView setScrollEnabled:NO]; [textView setScrollEnabled:YES]; – Ofir Malachi Aug 13 '17 at 08:43
  • 4
    @ofirmalachi, did you mean to write conflicting statements in your comment above? – stuckj May 16 '19 at 13:48
  • @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. – Bawenang Rukmoko Pardian Putra Sep 09 '22 at 01:20
  • Note using `scrollRangeToVisible` with large texts may make `UITextView` freeze because of animation. Better use `contentOffset` as shown here https://stackoverflow.com/a/42478029/3004003, but set `animated` to `false` – schmidt9 Oct 31 '22 at 17:38
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];
}
Community
  • 1
  • 1
Hong Duan
  • 4,234
  • 2
  • 27
  • 50
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)
zeeawan
  • 6,667
  • 2
  • 50
  • 56
  • It should work. Where are you using this code? Make sure you're using it after textView has been loaded. – zeeawan Mar 14 '17 at 21:03
  • 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 – xaphod Mar 14 '17 at 21:04
  • 1
    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. – zeeawan Mar 14 '17 at 21:53
  • It's better to add check if bottom positive or not, because if it is negative then there is no need to change contentOffset. – Ruslan Mansurov Jul 05 '17 at 09:06
  • Doesn't work for me (iOS11). My text spans many lines and uses a custom font. – neoneye Feb 27 '18 at 09:53
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()
neoneye
  • 50,398
  • 25
  • 166
  • 151
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);
Chris Amelinckx
  • 4,334
  • 2
  • 23
  • 21
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;
}
thesquaregroot
  • 1,414
  • 1
  • 21
  • 35
wolffan
  • 1,084
  • 10
  • 15
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];
Kevin Chen
  • 994
  • 8
  • 24
Brandon Boynton
  • 115
  • 1
  • 9
  • 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. – David Lawson Oct 13 '16 at 08:44
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..

Pablo Sanchez Gomez
  • 1,438
  • 16
  • 28
ArunGJ
  • 2,685
  • 21
  • 27
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
Nathan Hosselton
  • 1,089
  • 1
  • 12
  • 16
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
    }
}
yoAlex5
  • 29,217
  • 8
  • 193
  • 205