Search code examples
ioscocoa-touchuiscrollviewuitextviewuikeyboard

UIScrollView issue on tapping UITextView or UITextField on keyboard presentation


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

  1. Tapping on textView presents the keyboard and textview is scrolled to the top if it is hidden by keyboard. Now, changing the orientation (landscape left to landscape Right) makes the textView scroll further to top and hence invisible.
  2. The scrolling of TextView sometimes works for landscape left orientation but not for landscape right orientation and vice versa. This is because of the keyboardEndFrame values I'm using. Shouldn't I use the keyboardEndFrame origin values ? If not, what would be the alternative ?
  3. Sometimes the it works for the textField but not for the textView.

Solution

  • 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.