UITextView in iOS7 clips the last line of text string

The problem is due to iOS 7. In the text view delegate, add this code:

- (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];
        }];
    }
}

The solution I found here was to add a one line fix after you create a UITextView:

self.textview.layoutManager.allowsNonContiguousLayout = NO;

This one line fixed three issues I had creating a UITextView-based code editor with syntax highlighting on iOS7:

  1. Scrolling to keep text in view when editing (the issue of this post)
  2. UITextView occasionally jumping around after dismissing the keyboard
  3. UITextView random scrolling jumps when trying to scroll the view

Note, I did resize the whole UITextView when the keyboard is shown/hidden.


Try implementing the -textViewDidChangeSelection: delegate method from the UITextViewDelegate like this:

-(void)textViewDidChangeSelection:(UITextView *)textView {
    [textView scrollRangeToVisible:textView.selectedRange];
}

Heres a modified version of the selected answer by davidisdk.

- (void)textViewDidChange:(UITextView *)textView {
    NSRange selection = textView.selectedRange;

    if (selection.location + selection.length == [textView.text length]) {
        CGRect caretRect = [textView caretRectForPosition:textView.selectedTextRange.start];
        CGFloat overflow = caretRect.origin.y + caretRect.size.height - (textView.contentOffset.y + textView.bounds.size.height - textView.contentInset.bottom - textView.contentInset.top);

        if (overflow > 0.0f) {
            CGPoint offset = textView.contentOffset;
            offset.y += overflow + 7.0f;

            [UIView animateWithDuration:0.2f animations:^{
                [textView setContentOffset:offset];
            }];
        }
    } else {
        [textView scrollRangeToVisible:selection];
    }
}

I was getting a bug that when the textView's content size is larger then the bounds and the cursor is offscreen (such as using a keyboard and pressing the arrow key) the textView wouldn't animate to the text being inserted.