I have a UIScrollView
, which contains UITextFields
and UITextViews
. I have registered for the UIKeyboardDidChangeFrameNotification
. When I tap on a text field or text view, the did change frame notification action is triggered and I adjust the contentOffset
of the scroll view as shown below
- (void)keyboardDidChangeFrame:(NSNotification *)notification
{
CGRect keyboardEndFrame;
[[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGRect intersection;
UIView *theFirstResponder = [[UIApplication sharedApplication].keyWindow findFirstResponder];
if ([theFirstResponder isKindOfClass:[UITextView class]]) {
if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeLeft) {
keyboardEndFrame = CGRectMake(keyboardEndFrame.origin.y, 416, keyboardEndFrame.size.height, keyboardEndFrame.size.width);
}
else if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeRight)
{
keyboardEndFrame = CGRectMake(keyboardEndFrame.origin.y, 0, keyboardEndFrame.size.height, keyboardEndFrame.size.width);
}
}
else
keyboardEndFrame = CGRectMake(keyboardEndFrame.origin.y, keyboardEndFrame.origin.x, keyboardEndFrame.size.height, keyboardEndFrame.size.width);
screenRect = CGRectMake(screenRect.origin.y, screenRect.origin.x, screenRect.size.height, screenRect.size.width);
if(CGRectEqualToRect(lastKBDRect, keyboardEndFrame)) {
return;
}
lastKBDRect = keyboardEndFrame;
if (CGRectIntersectsRect(keyboardEndFrame, screenRect)) {
// Keyboard is visible
//Convert Frame of the first responder, in context of the view that needs to be shifted
UIView *firstResponder = [[UIApplication sharedApplication].keyWindow findFirstResponder];
CGRect theRect = [firstResponder convertRect:firstResponder.frame toView:[UIApplication sharedApplication].keyWindow];
theRect = CGRectMake(theRect.origin.y, theRect.origin.x > 768 ? 750 : theRect.origin.x, theRect.size.height, theRect.size.width);
intersection = CGRectIntersection(keyboardEndFrame, theRect);
//If intersection is null, then no need to shift anything. Simply return.
if(CGRectIsNull(intersection)) {
return;
}
//Shift the view so that the first responder view is completely visible, keeping the constraint that the origin of the first responder view is also visible.
//Remember the current offset, so when we shift the view back, we shift it to the proper position.
if (!wasContentViewShifted) {
lastContentOffset = contentScrollView.contentOffset;
lastContentSize = contentScrollView.contentSize;
wasContentViewShifted = YES;
}
CGFloat offset = theRect.origin.y + theRect.size.height - keyboardEndFrame.origin.y;
if((theRect.origin.y - offset) < 40) {
offset += 42;
}
[UIView animateWithDuration:0.3f animations:^{
contentScrollView.contentOffset = CGPointMake(0, contentScrollView.contentOffset.y + offset);
contentScrollView.contentSize = CGSizeMake(0, lastContentSize.height + (600 - theRect.size.height));
}];
} else {
// Keyboard is hidden. Move the view back only if it was shifted.
if(wasContentViewShifted) {
wasContentViewShifted = NO;
[UIView animateWithDuration:0.3f animations:^{
contentScrollView.contentOffset = lastContentOffset;
contentScrollView.contentSize = lastContentSize;
}];
}
}
}
The application supports only landscape orientation. The problems I'm facing here are
keyboardEndFrame
values I'm using. Shouldn't I use the keyboardEndFrame
origin values ? If not, what would be the alternative ?Instead of having such complex solution you can try using this. One simpler solution can also be found here.
Also it is not advisable to hard code the frames as you are doing. Refer to the apple documentation for more details.