Fiendishly Clever Title

Thursday, December 24, 2009

I came across a post describing how to update an existing UIView to use a UIScrollView. This is useful because the iPhone keyboard will otherwise obscure underlying content, including the text field being edited.

My setup is a little different. The original post used a form that extended to the bottom of the screen. This allowed Steve to shorten the UIScrollView's frame by the height of the keyboard. I'm using a UIScrollView hosted within a UITabBar. The view's height was (480 - tab_bar_height - status_bar_height); shortening it by the full keyboard height left it too short by tab_bar_height. This left a white gap between the UIScrollView and the keyboard.

My solution was to get the keyboard's absolute (window-relative) coordinates, then to convert them to view-relative coordinates. For example, assume that the UIScrollView starts at (0, 20) and the keyboard is from (0, 280) with a size of (320, 200). Using the UIView convertRect:fromView method, we find that the keyboard's origin is (0, 260) relative to the UIScrollView. We can just chop the UIScrollView's frame at a height of 260. This cuts it off at the keyboard's top.

The only wrinkle that I've encountered is that the convertRect:fromView method uses the child UIView's coordinates, not the UIScrollView's. It's easy enough to use the contentOffset property to correct for this for cases where the content has already been scrolled in the UIScrollView.

This also has the advantage that it should work even if the keyboard is already present. Steve keeps a flag to track whether the keyboard is displayed. This prevents the code from shortening an already-shortened UIScrollView if UIKeyboardDidShowNotification is fired with the keyboard already visible. But since this code always calculates the correct height, we don't have to worry about multiple calls.


- (CGRect) getKeyboardFrameRelativeToScrollView:(UIScrollView *) aScrollView
                                 keyboardBounds:(CGRect)bounds
                                 keyboardCenter:(CGPoint)center
{
    CGRect keyboardFrame;
    
    keyboardFrame.size = bounds.size;
    keyboardFrame.origin.x = center.x - (keyboardFrame.size.width / 2);
    keyboardFrame.origin.y = center.y - (keyboardFrame.size.height / 2);
    
    CGRect scrollViewRelativeKeyboardFrame = [aScrollView convertRect:keyboardFrame fromView:nil];
    scrollViewRelativeKeyboardFrame.origin.x -= aScrollView.contentOffset.x;
    scrollViewRelativeKeyboardFrame.origin.y -= aScrollView.contentOffset.y;

    return scrollViewRelativeKeyboardFrame;
}


// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
    NSDictionary *info = [aNotification userInfo];

    CGRect keyBounds = [self getKeyboardFrameRelativeToScrollView:scrollView
                        keyboardBounds:[[info objectForKey:UIKeyboardBoundsUserInfoKey] CGRectValue]
                        keyboardCenter:[[info objectForKey:UIKeyboardCenterEndUserInfoKey] CGPointValue]];
    

    
    // Resize the scroll view (which is the root view of the window)
    CGRect viewFrame = [scrollView frame];
    viewFrame.size.height = keyBounds.origin.y;
    scrollView.frame = viewFrame;
    // Scroll the active text field into view.
    CGRect textFieldRect = [descriptionTextView frame];
    
    // Leave a little padding between the text area and the keyboard.
    textFieldRect.size.height += 20;
    [scrollView scrollRectToVisible:textFieldRect animated:YES];
}


Followers