UITextView in iOS7 has been really weird. As you type and are entering the last line of your UITextView, the scroll view doesn\'t scroll to the bottom like it should and it
Imho this is the definitive answer for all of the typical UITextView-scrolling / keyboard related issues in iOS 7. Its clean, its easy to read, easy to use, easy to maintain and can easily be reused.
The basic trick: Simply change the size of the UITextView, not the content inset!
Here's a hands-on example. It takes for granted that you have a NIB/Storyboard-based UIViewController using Autolayout and the UITextView fills out the entire root view in the UIViewController. If not you will have to adapt how you change the textViewBottomSpaceConstraint to your needs.
How to:
Add these properties:
@property (nonatomic, weak) IBOutlet NSLayoutConstraint *textViewBottomSpaceConstraint;
@property (nonatomic) CGFloat textViewBottomSpaceConstraintFromNIB;
Connect the textViewBottomSpaceConstraint in Interface Builder (dont forget!)
Then in viewDidLoad:
// Save the state of the UITextView's bottom constraint as set up in your NIB/Storyboard
self.textViewBottomSpaceConstraintFromNIB = self.textViewBottomSpaceConstraint.constant;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShowNotification:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillHideNotification:)
name:UIKeyboardWillHideNotification
object:nil];
Add these methods to handle Keyboard resizing (thanks to https://github.com/brennanMKE/Interfaces/tree/master/Keyboarding - these methods are by brennan!):
- (void)keyboardWillShowNotification:(NSNotification *)notification {
CGFloat height = [self getKeyboardHeight:notification forBeginning:TRUE];
NSTimeInterval duration = [self getDuration:notification];
UIViewAnimationOptions curve = [self getAnimationCurve:notification];
[self keyboardWillShowWithHeight:height duration:duration curve:curve];
}
- (void)keyboardWillHideNotification:(NSNotification *)notification {
CGFloat height = [self getKeyboardHeight:notification forBeginning:FALSE];
NSTimeInterval duration = [self getDuration:notification];
UIViewAnimationOptions curve = [self getAnimationCurve:notification];
[self keyboardWillHideWithHeight:height duration:duration curve:curve];
}
- (NSTimeInterval)getDuration:(NSNotification *)notification {
NSDictionary *info = [notification userInfo];
NSTimeInterval duration;
NSValue *durationValue = [info objectForKey:UIKeyboardAnimationDurationUserInfoKey];
[durationValue getValue:&duration];
return duration;
}
- (CGFloat)getKeyboardHeight:(NSNotification *)notification forBeginning:(BOOL)forBeginning {
NSDictionary *info = [notification userInfo];
CGFloat keyboardHeight;
NSValue *boundsValue = nil;
if (forBeginning) {
boundsValue = [info valueForKey:UIKeyboardFrameBeginUserInfoKey];
}
else {
boundsValue = [info valueForKey:UIKeyboardFrameEndUserInfoKey];
}
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if (UIDeviceOrientationIsLandscape(orientation)) {
keyboardHeight = [boundsValue CGRectValue].size.width;
}
else {
keyboardHeight = [boundsValue CGRectValue].size.height;
}
return keyboardHeight;
}
- (UIViewAnimationOptions)getAnimationCurve:(NSNotification *)notification {
UIViewAnimationCurve curve = [[notification.userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue];
switch (curve) {
case UIViewAnimationCurveEaseInOut:
return UIViewAnimationOptionCurveEaseInOut;
break;
case UIViewAnimationCurveEaseIn:
return UIViewAnimationOptionCurveEaseIn;
break;
case UIViewAnimationCurveEaseOut:
return UIViewAnimationOptionCurveEaseOut;
break;
case UIViewAnimationCurveLinear:
return UIViewAnimationOptionCurveLinear;
break;
}
return kNilOptions;
}
Finally, add these methods for reacting to the keyboard notifications and resize the UITextView
- (void)keyboardWillShowWithHeight:(CGFloat)height duration:(CGFloat)duration curve:(UIViewAnimationOptions)curve
{
CGFloat correctionMargin = 15; // you can experiment with this margin so the bottom text view line is not flush against the keyboard which doesn't look nice
self.textViewBottomSpaceConstraint.constant = height + correctionMargin;
[self.view setNeedsUpdateConstraints];
[UIView animateWithDuration:duration delay:0 options:curve animations:^{
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
}];
}
- (void)keyboardWillHideWithHeight:(CGFloat)height duration:(CGFloat)duration curve:(UIViewAnimationOptions)curve
{
self.textViewBottomSpaceConstraint.constant = self.textViewBottomSpaceConstraintFromNIB;
[self.view setNeedsUpdateConstraints];
[UIView animateWithDuration:duration delay:0 options:curve animations:^{
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
}];
}
Also add these methods to automatically scroll to where the user clicked
- (void)textViewDidBeginEditing:(UITextView *)textView
{
[textView scrollRangeToVisible:textView.selectedRange];
}
- (void)textViewDidChangeSelection:(UITextView *)textView
{
[textView scrollRangeToVisible:textView.selectedRange];
}
textView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
This resolved the issue for me
textView.contentInset = UIEdgeInsetsMake(0.0, 0.0, 10.0, 0.0);
This will also address your issue
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];
}];
}
}
This line causes the last line of text to not show up for me:
textView.scrollEnabled = false
Try removing this and see what happens...