Search code examples
ioscocoaios6ios7

How to stick a button that is normally below a UIWebView above the keyboard when it opens?


I would like to add a button and some other views below a UIWebView. When the keyboard opens, these views should smoothly animate up while the keyboard rises. The UIWebView should resize smoothly. I currently have the following:

No keyboard:

no_keyboard

Keyboard:

keyboard

It looks like it's working fine, but in reality, this is what it looks like in the middle of resizing:

during_resizing

The UIWebView's bottom jumps up immediately, the keyboard resizes smoothly, and the bottom views lag a little bit behind.

I have the following code:

- (void)registerForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleKeyboardWillChangeFrameNotification:)
                                                 name:UIKeyboardWillChangeFrameNotification object:nil];
}

- (void)handleKeyboardWillChangeFrameNotification:(NSNotification*)aNotification
{
    NSDictionary* info = [aNotification userInfo];
    NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    if (!self.is_keyboard_open) {
        self.bottom_constraint_constant = self.bottomConstraint.constant;
        self.bottomConstraint.constant = kbSize.height + self.bottomConstraint.constant;
    } else {
        self.bottomConstraint.constant = self.bottom_constraint_constant;
    }

    [UIView animateWithDuration:animationDuration animations:^{
        [self.view layoutIfNeeded];
    }];

    self.is_keyboard_open = !self.is_keyboard_open;
}

The bottom constraint is the constraint between the text and the bottom of the screen.

Another issue I have is that when the keyboard is toggled more than once, the UIWebView does not resize correctly when it is opened, causing a partial render, as well as a large gray box at the bottom (which I can scroll up and down along with rest of the content, which would fit normally if this gray box was not present), like so:

weird_bug

What can I do? Thank you for your help!


Solution

  • You should animate the constraint change. You have all the information necessary in the notification user info (UIKeyboardAnimationCurveUserInfoKey, UIKeyboardAnimationDurationUserInfoKey).

    Your second issue is a problem with UIWebView, I think. I've had this issue too. In iOS7, Apple seems to attempt setting a content inset for the webview scroll view, but sometimes it fails miserably. I ended up doing nasty tricks to prevent the webview from doing those.

    Addendum

    How to use the animation curve

    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:0.5];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
    // ... do stuff here
    [UIView commitAnimations];
    

    Webview tricks

    Our webview use is even more complex because we use contentEditable=YES to edit HTML content. What we did was taking full control of the internal webview subview.

    Normally, the view hierarchy of a UIWebView is:

    • UIWebView
      • Private UIScrollView subclass
        • Private view where the actual content is drawn

    We ended up taking the private view and putting it inside our own scroll view which we can manage by ourselves. You need to update the content size as the private view grows or shrinks. We used key-value observation on the frame property to listen to changes to the size. To have the correct content inset, we used the keyboard frame to determine how much the bottom content inset is.

    I must postface this, and say that ideally, we should not do this. It's error prone. From experience, we know it works from iOS5 to iOS7.1. I cannot promise it will work on iOS8. But since there are bugs which cannot be resolved otherwise, this is the best solution we found.