7
votes

I am trying to move a UIScrollView when the keyboard hides a UITextField by changing the size using the contentInsets as it is shown.

However, it's not working for the keyboard height. The keyboard height comes as 216, but it only stops scrolling at the correct location if I set the bottom inset to 515 for iPhone portrait mode and 310 for iPhone landscape mode. Why would these dimensions be so different? I don't want to hardcode these arbitrary values in.

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.view.frame = self.parentViewController.view.frame;

    [self.textView becomeFirstResponder];

    NSLog(@"scrollview: %f,%f, parent: %f,%f", self.view.frame.size.height, self.view.frame.size.width, self.parentViewController.view.frame.size.height, self.parentViewController.view.frame.size.width);

    [[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWasShown:)
                                             name:UIKeyboardDidShowNotification
                                           object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWillHide:)
                                             name:UIKeyboardWillHideNotification
                                           object:nil];
}

- (void)keyboardWasShown:(NSNotification *)notification
{
    if([[UIDevice currentDevice]userInterfaceIdiom]==UIUserInterfaceIdiomPhone) {

        // Step 1: Get the size of the keyboard.
        CGFloat keyboardHeight = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size.height;

        // Step 2: Adjust the bottom content inset of your scroll view by the keyboard height.
        UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardHeight, 0.0);
        ((UIScrollView*)self.view).contentInset = contentInsets;
        ((UIScrollView*)self.view).scrollIndicatorInsets = contentInsets;

        // Step 3: Scroll the target text field into view.
        CGRect aRect = self.view.frame;
        aRect.size.height = aRect.size.height - keyboardHeight;
        if (!CGRectContainsPoint(aRect, self.textView.frame.origin) ) {
            CGPoint scrollPoint = CGPointMake(0.0, self.textView.frame.origin.y - keyboardHeight);
            [((UIScrollView*)self.view) setContentOffset:scrollPoint animated:YES];
        }
    }
}

- (void) keyboardWillHide:(NSNotification *)notification {
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    ((UIScrollView*)self.view).contentInset = contentInsets;
    ((UIScrollView*)self.view).scrollIndicatorInsets = contentInsets;
}

Edit:

Before the keyboard is open, i print this out:

NSLog(@"scrollview: %f,%f, parent: %f,%f", self.view.frame.size.height, self.view.frame.size.width, self.parentViewController.view.frame.size.height, self.parentViewController.view.frame.size.width);

and it prints this:

scrollview: 431.000000,320.000000, parent: 431.000000,320.000000
2
is is scrolling fine without keyboard, it might be possible you have given height greater than the height of parent view.Manish Agrawal
Yep. It's scrolling finekb_
i want to know the frame of scrollview and its parent view frame before opening the keyboard.Manish Agrawal
The UIScrollView is inside a UITabBarController. Please see my edits.kb_
in your code is self.view is kind of scrollview?Manish Agrawal

2 Answers

23
votes

I think that the main issue is that you need to use UIKeyboardFrameEndUserInfoKey instead of UIKeyboardFrameBeginUserInfoKey.

That being said, I have written a different version of this method that will work even if the scrollview is not placed at the bottom of the screen and may work better for you:

Note that I use self.activeTextField instead of self.textField.

// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
    // Calculate the frame of the scrollview, in self.view's coordinate system
    UIScrollView *scrollView    = self.scrollView;
    CGRect scrollViewRect       = [self.view convertRect:scrollView.frame fromView:scrollView.superview];

    // Calculate the frame of the keyboard, in self.view's coordinate system
    NSDictionary* info          = [aNotification userInfo];
    CGRect kbRect               = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    kbRect                      = [self.view convertRect:kbRect fromView:nil];

    // Figure out where the two frames overlap, and set the content offset of the scrollview appropriately
    CGRect hiddenScrollViewRect = CGRectIntersection(scrollViewRect, kbRect);
    if (!CGRectIsNull(hiddenScrollViewRect))
    {
        UIEdgeInsets contentInsets       = UIEdgeInsetsMake(0.0,  0.0, hiddenScrollViewRect.size.height,  0.0);
        scrollView.contentInset          = contentInsets;
        scrollView.scrollIndicatorInsets = contentInsets;
    }

    [self scrollToActiveTextField];
}

// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
    self.scrollView.contentInset          = UIEdgeInsetsZero;
    self.scrollView.scrollIndicatorInsets = UIEdgeInsetsZero;
}

- (void)scrollToActiveTextField
{
    if (self.activeTextField)
    {
        CGRect visibleRect = self.activeTextField.frame;
        visibleRect        = [self.scrollView convertRect:visibleRect fromView:self.activeTextField.superview];
        visibleRect        = CGRectInset(visibleRect, 0.0f, -5.0f);
        [self.scrollView scrollRectToVisible:visibleRect animated:YES];
    }
}
0
votes

set delegate of your scrollView as self whereever you are crating scrollview in your code

 ((UIScrollView*)self.view).delegate=self;

then

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.view.frame = self.parentViewController.view.frame;

    [self.textView becomeFirstResponder];

    NSLog(@"scrollview: %f,%f, parent: %f,%f", self.view.frame.size.height, self.view.frame.size.width, self.parentViewController.view.frame.size.height, self.parentViewController.view.frame.size.width);

    [[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWasShown:)
                                             name:UIKeyboardDidShowNotification
                                           object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWillHide:)
                                             name:UIKeyboardWillHideNotification
                                           object:nil];
}

- (void)keyboardWasShown:(NSNotification *)notification
{
    if([[UIDevice currentDevice]userInterfaceIdiom]==UIUserInterfaceIdiomPhone) {

        // Step 1: Get the size of the keyboard.
        CGFloat keyboardHeight = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size.height;

        // Step 2: Adjust the bottom content inset of your scroll view by the keyboard height.
        UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardHeight, 0.0);
        ((UIScrollView*)self.view).contentInset = contentInsets;
        ((UIScrollView*)self.view).scrollIndicatorInsets = contentInsets;

        ((UIScrollView*)self.view).scrollEnabled=YES;
    }
}

- (void) keyboardWillHide:(NSNotification *)notification {
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    ((UIScrollView*)self.view).contentInset = contentInsets;
    ((UIScrollView*)self.view).scrollIndicatorInsets = contentInsets;
    [((UIScrollView*)self.view) setContentOffset:CGPointMake(0.0f, 0.0f) animated:TRUE];
    ((UIScrollView*)self.view).contentSize = CGSizeMake(((UIScrollView*)self.view).contentSize.width, ((UIScrollView*)self.view).contentSize.height);
    ((UIScrollView*)self.view).scrollEnabled=NO;

}

#pragma mark - set scrollView content position
-(void)scrollViewToCenterOfScreen:(UIView *)theView
{
    CGFloat theViewY = theView.center.y;
    CGRect applicationFrame = [[UIScreen mainScreen] applicationFrame];
    CGFloat avaliableHeight = applicationFrame.size.height - 300;
    CGFloat y = theViewY - avaliableHeight / 2.0;
    if(y<0)
        y = 0;
    [((UIScrollView*)self.view) setContentOffset:CGPointMake(0,y) animated:YES];
}

#pragma -mark UITextField Delegate methods

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
    [self scrollViewToCenterOfScreen:textField];
    return YES;
}

Now set delegate of your text field. Whenever textFieldShouldBeginEditing: your scrollview automatically move on that. scrollViewToCenterOfScreen: method set your scrollview position at textfiled position.

It definitely set your scrollview content insets as well as setContentOffset. Please let me know if you still facing this problem. Thanks