Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a UITextView scroll while typing/editing

UPDATE This seemed to be an issue with IOS 7 only. A great workaround has been added to accepted answer.

I have created a custom control that contains a UITextView and UILabel which contains the title of the textview ie my control. My control automatically changes size to adapt the textview and the title. Before this happens I change the size of the textview to fit the text. This works optimally.

I've added functionality so the textview automatically scrolls to the last line. Or that's at least what I'm trying. It works fine as long as the last line contains anything but empty text. If the text is empty, it rolls down so you can only see about half of the cursor.

What am I doing wrong?

So you can understand it better I have made some images:

This is me typing a word and making some linebreaks. (Still not enough to make it scroll)

Before making a line break

And the I make a line break. (pressing enter) Look close at how the cursor is halved. This is the issue!

The Issue

I have made the next picture so you can see exactly what I expected.

What I Want!

like image 575
chrs Avatar asked Aug 06 '13 01:08

chrs


3 Answers

Problems with other answers:

  • when only scanning for "\n", if you type a line of text that exceeds the width of the text view, then scrolling will not occur.
  • when always setting contentOffset in textViewDidChange:, if you edit the middle of the text you do not want to scroll to the bottom.

The solution is to add this to the text view delegate:

- (void)textViewDidChange:(UITextView *)textView {
    CGRect line = [textView caretRectForPosition:
        textView.selectedTextRange.start];
    CGFloat overflow = line.origin.y + line.size.height
        - ( textView.contentOffset.y + textView.bounds.size.height
        - textView.contentInset.bottom - textView.contentInset.top );
    if ( overflow > 0 ) {
        // We are at the bottom of the visible text and introduced a line feed, scroll down (iOS 7 does not do it)
        // Scroll caret to visible area
        CGPoint offset = textView.contentOffset;
        offset.y += overflow + 7; // leave 7 pixels margin
        // Cannot animate with setContentOffset:animated: or caret will not appear
        [UIView animateWithDuration:.2 animations:^{
            [textView setContentOffset:offset];
        }];
    }
}
like image 68
davidisdk Avatar answered Nov 11 '22 15:11

davidisdk


I tried to put in your textViewDidChange: a snippet like:

if([textView.text hasSuffix:@"\n"])
    [self.textView setContentOffset:CGPointMake(0,INT_MAX) animated:YES];

It's not really clean, I'm working toward finding some better stuff, but for now it works :D

UPDATE: Since this is a bug that only happens on iOS 7 (Beta 5, for now), you can do a workaround with this code:

if([textView.text hasSuffix:@"\n"]) { 
    double delayInSeconds = 0.2; 
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 
        CGPoint bottomOffset = CGPointMake(0, self.textView.contentSize.height - self.textView.bounds.size.height); 
        [self.textView setContentOffset:bottomOffset animated:YES]; 
    }); 
}

Then, on iOS 6 you can choose either to set the delay to 0.0 or to use just the content of the block.

like image 41
Vik Avatar answered Nov 11 '22 14:11

Vik


I used the following code in the textViewDidChange: method and it seemed to work well.

- (void)textViewDidChange:(UITextView *)textView {
    CGPoint bottomOffset = CGPointMake(0, self.theTextView.contentSize.height - self.theTextView.bounds.size.height);
    [self.theTextView setContentOffset:bottomOffset animated:YES];
}

This seems to scroll the UITextView slightly further so that your cursor isn't cut off.

like image 4
hgwhittle Avatar answered Nov 11 '22 13:11

hgwhittle