Dealing with UITextFields in iOS can be frustrating. By default, if the UITextField in question is more than halfway down the screen, the keyboard will cover it while typing. There are several solutions out there on the internets that deal with this, one of which, from the Cocoa with Love blog, the solution I present here is based on. For those of you who want more background on that, feel free to check out that blog post.
In that solution, the screen simply scrolls up when the keyboard comes up, and down when it goes down. But what if there is content that higher up on the screen that you want to be able to scroll back and see? Well if that’s your problem, then this post is for you. (For those of you familiar with the way Android handles this problem, this solution imitates that pretty closely.)
The full code for the project can be found here.
The first step is to embed everything that you want to scroll when the keyboard is up in a UIScrollView. You can do this by selecting all those views, and then selecting Editor > Embed In > Scroll View. Then, control-drag the scroll view to your view controller .h file. While you’re at it, control drag the TextField(s) you want to keep from getting covered by keyboard to the .h file too.
Next, in your implementation file, implement the UITextField’s delegate methods (after, of course, setting self as the delegate). First, textFieldDidBeginEditing:
– (void)textFieldDidBeginEditing:(UITextField *)textField
{
//the following few lines calculate how far we’ll need to scroll
CGRect textFieldRect = [self.view.window convertRect:textField.bounds fromView:textField];
CGRect viewRect = [self.view.window convertRect:self.view.bounds fromView:self.view];CGFloat midline = textFieldRect.origin.y + 0.5 * textFieldRect.size.height;
CGFloat numerator = midline – viewRect.origin.y – MINIMUM_SCROLL_FRACTION * viewRect.size.height;
CGFloat denominator = (MAXIMUM_SCROLL_FRACTION – MINIMUM_SCROLL_FRACTION) * viewRect.size.height;
CGFloat heightFraction = numerator / denominator;//make sure it’s scrolling reasonably
if (heightFraction < 0.0) {
heightFraction = 0.0;
}else if (heightFraction > 1.0) {
heightFraction = 1.0;
}//the orientation of the phone changes how much we want to scroll.
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) {
animatedDistance = floor(PORTRAIT_KEYBOARD_HEIGHT * heightFraction);
}else {
animatedDistance = floor(LANDSCAPE_KEYBOARD_HEIGHT * heightFraction);
}//get the scrollview’s current size, and add the distance we want to scroll to it
CGSize newSize = self.scrollView.contentSize;
newSize.height += animatedDistance;
self.scrollView.contentSize = newSize;//finally, scroll that distance
CGPoint p = self.scrollView.contentOffset;
p.y += animatedDistance;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];[self.scrollView setContentOffset:p animated:NO];
[UIView commitAnimations];
}
And, to undo the process:
– (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
//reverse the process in textFieldDidBeginEditing
CGSize newSize = self.scrollView.contentSize;
newSize.height -= animatedDistance;
CGPoint p = self.scrollView.contentOffset;
p.y = 0;//note that we have to animate BOTH the scrollview resizing AND the offset change.
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];
self.scrollView.contentSize = newSize;
[self.scrollView setContentOffset:p animated:NO];
[UIView commitAnimations];
return YES;
}
Voila! Your UITextField will now avoid keyboard cover up, and still allow you to scroll back to the top of the page. If it’s not working, don’t forget to make sure you set the UITextField’s delegate property!