I have a UIViewCOntroller
that contains a UITextView
. When the keyboard appears I resize it like this:
#pragma mark - Responding to keyboard events
- (void)keyboardDidShow:(NSNotification *)notification
{
NSDictionary* info = [notification userInfo];
CGRect keyboardSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect newTextViewFrame = self.textView.frame;
newTextViewFrame.size.height -= keyboardSize.size.height + 70;
self.textView.frame = newTextViewFrame;
self.textView.backgroundColor = [UIColor yellowColor];
}
- (void)keyboardWillHide:(NSNotification *)notification
{
NSDictionary* info = [notification userInfo];
CGRect keyboardSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect newTextViewFrame = self.textView.frame;
newTextViewFrame.size.height += keyboardSize.size.height - 70;
self.textView.frame = newTextViewFrame;
}
The textView seems to rezise to the right size, but when the user types the cursor ends up "outside" the textView frame. See picture below:
The yellow area is the UITextView
frame (I don't know what the blue line next to the R key is). I find this quite wired. I'm using iOS7 if that makes any difference.
Any ideas or tips?
Update
I have a UITextView subclass that draws horizontal lines with the following method (if that makes any difference):
- (void)drawRect:(CGRect)rect {
//Get the current drawing context
CGContextRef context = UIGraphicsGetCurrentContext();
//Set the line color and width
CGContextSetStrokeColorWithColor(context, [UIColor colorWithRed:229.0/255.0 green:244.0/255.0 blue:255.0/255.0 alpha:1].CGColor);
CGContextSetLineWidth(context, 1.0f);
//Start a new Path
CGContextBeginPath(context);
//Find the number of lines in our textView + add a bit more height to draw lines in the empty part of the view
NSUInteger numberOfLines = (self.contentSize.height + rect.size.height) / self.font.lineHeight;
CGFloat baselineOffset = 6.0f;
//iterate over numberOfLines and draw each line
for (int x = 0; x < numberOfLines; x++) {
//0.5f offset lines up line with pixel boundary
CGContextMoveToPoint(context, rect.origin.x, self.font.lineHeight*x + 0.5f + baselineOffset);
CGContextAddLineToPoint(context, rect.size.width, self.font.lineHeight*x + 0.5f + baselineOffset);
}
// Close our Path and Stroke (draw) it
CGContextClosePath(context);
CGContextStrokePath(context);
}
A simpler solution to this problem is to update the text view frame in response to the textViewDidBegingEditing delegate method. For further details, see the following:
How to re-size UITextView when keyboard shown with iOS 7
Anders and Leo Natan have great solutions. However, I needed to modify their answers a little to get the scrolling to work properly with contentInset. The problem I faced was that
textViewDidBeginEditing:
gets called beforekeyboardWasShown:
so the contentInset change does not get reflected the first time through. Here is what I did:In .h
In .m
Angel Naydenov's comment above is right, especially in cases such as switching from English to Japanese keyboard that shows suggests.
When switching keyboards,
UIKeyboardWillShowNotification
is called butUIKeyboardWillHideNotification
is not called.So you must adjust the inset to use the absolute value and not use
+=
.Unrelatedly,
[self.textView setContentOffset:newOffset animated:YES];
will not actually change the graphics in iOS 7.1 after the keyboard is shown for the second time, which is probably a bug. A workaround I used is replacingwith
Leo Natan, you started out well but your execution was relatively inefficient. Here is a better way of doing it with less code:
Instead of resizing the frame, why not give your text view a
contentInset
(and a matchingscrollIndicatorInsets
)? Remember that text views are actually scrollviews. This is the correct way to handle keyboard (or other) interference.For more information on
contentInset
, see this question.This seems to not be enough. Still use insets, as this is more correct (especially on iOS7, where the keyboard is transparent), but you will also need extra handling for the caret:
A lot of work, Apple should provide better way of handling the caret, but this works.
All of the others answers I tried behaved somewhat strangely for me. Using an
NSTimer
to perform the scroll also meant that the user couldn't scroll up, since the caret would then end up off-screen and it would immediately scroll back down again. In the end I stuck with the original approach of changing theUITextView
frame on the keyboard notification events, then added the following methods:Works like a charm for me