Skip to content

[Bug]: iOS resize:native applies the WebView resize ~200ms AFTER the keyboard settles, and never animates it #79

Description

@jimmycal

Bug Report

Capacitor Version

💊   Capacitor Doctor  💊

Latest Dependencies:

  @capacitor/cli: 8.5.0
  @capacitor/core: 8.5.0
  @capacitor/android: 8.5.0
  @capacitor/ios: 8.5.0

Installed Dependencies:

  @capacitor/android: not installed
  @capacitor/cli: 8.4.0
  @capacitor/core: 8.4.0
  @capacitor/ios: 8.4.0

@capacitor/keyboard@8.0.5

Platform(s)

iOS. Verified on iPhone 17 Pro Max, iOS 26.5 (physical device).

Current Behavior

With resize: KeyboardResize.Native, the WebView resize is scheduled as a delayed one-frame snap rather than animated alongside the keyboard.

Keyboard.monKeyboardWillShow: (L256):

double duration = [[notification.userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue] + 0.2;
[self setKeyboardHeight:(int)height delay:duration];

UIKeyboardAnimationDurationUserInfoKey is read, has 0.2 added to it, and is then used as a delay — not as an animation duration. setKeyboardHeight:delay: (L306) schedules:

[weakSelf performSelector:@selector(_updateFrame) withObject:nil afterDelay:delay inModes:@[NSRunLoopCommonModes]];

and _updateFrame (L356) applies the frame bare:

case ResizeNative:
  [self.webView setFrame:CGRectMake(wf.origin.x, wf.origin.y, f.size.width - wf.origin.x, f.size.height - wf.origin.y - self.paddingBottom)];
  break;

Grepping the file for UIView animate / animateWithDuration / CATransaction returns nothing — no animation API is used anywhere in the plugin.

Resulting timeline on keyboard open:

t
0 → ~0.25s keyboard slides up over a still-full-size WebView
~0.25s keyboard has settled
~0.45s WebView snaps to its smaller size in one frame; web content reflows

The visible effect is that the page jumps ~200ms after the keyboard has already stopped moving, while everything else on screen is static. UIKeyboardAnimationCurveUserInfoKey is never read at all.

Related but distinct: #48 (webview not resized when the keyboard is resized).

Expected Behavior

The WebView resize should be applied inside the keyboard's own animation, using the notification's duration and curve, so the two travel together:

double duration = [[notification.userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
UIViewAnimationCurve curve = [[notification.userInfo valueForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue];

[UIView animateWithDuration:duration
                      delay:0
                    options:(UIViewAnimationOptions)(curve << 16)
                 animations:^{ [self _updateFrame]; }
                 completion:nil];

Code Reproduction

No minimal repro app attached — the behaviour is directly readable from the source above, and reproduces on any resize: native app with a full-screen form. Happy to build one if that's needed to progress this.

To observe it: focus an input on a page with visible content in the bottom third, and watch the content after the keyboard finishes animating.

Other Technical Details

Visibility depends on autoBackdropColor. During the snap, the resized WebView's backing is briefly unpainted and the key window shows through. With the default autoBackdropColor: "off" and no window background set, that region is the UIView default — true black — so the snap reads as a black flash. Setting autoBackdropColor: "dom" paints currentKeyWindow.backgroundColor from the DOM body on every show, which makes the snap invisible even though it still happens. That masking is why this may be under-reported.

I'm aware the + 0.2 is probably deliberate and I don't know what it's load-bearing for. The same file carries shouldIgnoreResizeForHeight (iPad/QuickType), stageManagerOffset, and a hideTimer debounce on the dismiss path — all suggesting timing has been fought here before. The delay may exist to let the keyboard frame settle before measuring. So I'm filing this as an issue rather than a PR: I can't validate an animated version against iPad, Stage Manager, or keyboard-type switches, and a naive change there looks likely to regress cases you've already fixed.

Additional Context

We hit this in a Capacitor app and tried to work around it downstream by setting resize: "none" and re-implementing the resize in CAPBridgeViewController inside UIView.animate with the notification's duration and curve. That did remove the jump on open, but broke dismissal three separate ways — the WebView stopped restoring to full height, leaving the page rendered at keyboard-up size. The plugin's own implementation correctly handles open, dismiss and keyboard-type switch; ours only ever handled open. Which is the argument for fixing it here rather than in userland.

We've since reverted to resize: native and mask the symptom with autoBackdropColor: "dom".

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions