Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

[@capacitor/keyboard] iOS: WebView frame permanently stuck at keyboard height after backgrounding (resize: native)

未关闭
#2,565 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

评估

难度
3/5
预计耗时
1-2 天
新手友好度
68/100
Issue 类型
缺陷
描述清晰度
基本清楚
活跃度
冷清
技术栈
ios, objective-c
领域
mobile-dev

调研方向

从 iOS 键盘插件的实现开始,检查 -load、setKeyboardHeight:delay: 和 _updateFrame;跟踪 app-did-become-active 路径如何与缓存的 paddingBottom 交互。使用 resize: "native" 重现报告的后台/前台循环,并验证 WebView 是否在没有黑色条带的情况下恢复到完整高度。还要检查 currentKeyWindow 为 nil 的情况,以及 issue 中提到的未初始化 frame。

由索引模型根据 Issue 内容生成。

描述

platform: ios
Plugin(s)

@capacitor/[email protected] (iOS)

Description

With the default resize: "native" policy, the WKWebView frame can get permanently stuck at its keyboard-shrunk height after the app is backgrounded and returned to. The web content then occupies only the top of the screen, with a black band below it exactly one keyboard-height tall.

The band is pure black rather than ios.backgroundColor, because CAPBridgeViewController.loadView() does view = webView — shrinking the WebView shrinks the view controller's root view, exposing the bare UIWindow.

Measured on a 956pt-tall device: web content ends at exactly 611pt, black band is 345pt. That is precisely the ResizeNative arithmetic in _updateFrame:

webView.frame.height = window.height - origin.y - paddingBottom   // 956 - 0 - 345 = 611
Root cause

setKeyboardHeight:delay: assigns paddingBottom synchronously, but defers the frame update via performSelector:afterDelay::

- (void)setKeyboardHeight:(int)height delay:(NSTimeInterval)delay
{
  if (self.paddingBottom == height) {
    return;                       // (2) guard makes the desync permanent
  }
  self.paddingBottom = height;    // (1) state updated NOW
  ...
  [weakSelf performSelector:@selector(_updateFrame) withObject:nil afterDelay:delay ...];  // frame updated LATER
}

Backgrounding with the keyboard up fires keyboardWillHide, which calls setKeyboardHeight:0 delay:0.01. If the app suspends inside that 10ms window, the run loop stops and _updateFrame is never delivered — leaving paddingBottom == 0 while the frame is still short by the keyboard height.

It is then unrecoverable, because:

  • _updateFrame is reachable from exactly one call site, setKeyboardHeight:
  • setKeyboardHeight: early-returns whenever paddingBottom already equals the requested height — which it now always does for 0
  • setResizeMode: only flips the enum; it never touches the frame

So every subsequent keyboardWillHide is a no-op and the WebView stays short for the remaining life of the process. The guard compares the intended height against itself rather than against the actual frame, so the model believes it is already reconciled.

Reproduction / evidence

Reproduced on an iPhone 17 Pro Max simulator (iOS 26, Xcode 26.6) by injecting the exact stuck state — frame short by 345pt while paddingBottom is 0 — and performing a real background/foreground cycle (verified same PID, i.e. a genuine resume rather than a cold start):

didBecomeActive  paddingBottom=0  frame BEFORE={{0, 0}, {440, 611}}

The frame stays at 611 indefinitely; no keyboard event can repair it.

User-level workaround: focus any text input and dismiss the keyboard. That drives paddingBottom 0 → 345 → 0, passing the guard in both directions, and the frame is restored.

Expected behavior

The WebView frame should return to full height once the keyboard is gone, regardless of whether the app was suspended while the deferred update was pending.

Suggested fix

Re-assert the frame when the app returns to the foreground. _updateFrame is already idempotent — it recomputes purely from the current window bounds and paddingBottom — so it is a no-op when state is consistent and a repair when a deferred update was dropped:

// in -load
[nc addObserver:self selector:@selector(onAppDidBecomeActive:)
           name:UIApplicationDidBecomeActiveNotification object:nil];

- (void)onAppDidBecomeActive:(NSNotification *)notification
{
  [self _updateFrame];   // deliberately not via setKeyboardHeight:, whose guard is the problem
}

Verified: with this in place the same background/foreground cycle restores the frame from 611 to 956, and the black band goes from 345pt to 0pt.

Alternatively (or additionally), the guard in setKeyboardHeight: could compare against the real webView.frame.size.height instead of the cached paddingBottom, so state can never silently diverge from the view.

Unrelated minor bug spotted nearby

In _updateFrame, f is declared uninitialized:

CGRect f, wf = CGRectZero;    // only `wf` is initialized
UIWindow *window = [self currentKeyWindow];
if (window) {
  f = [window bounds];
}
...
[self.webView setFrame:CGRectMake(wf.origin.x, wf.origin.y, f.size.width - wf.origin.x, f.size.height - wf.origin.y - self.paddingBottom)];

If currentKeyWindow returns nil, f holds stack garbage which is then written straight into the WebView frame. Worth initializing to CGRectZero and bailing when there is no window to measure.

Platforms

iOS

主要语言
Java
星标
682
派生
689
平均合并
5 天 11 小时
30 天内合并 PR
3

环境准备

  • 没有 Dockerfile 或 Docker Compose 文件
  • 没有 Pull Request 模板
  • 阅读贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

ionic-team/capacitor-plugins 的其他 Issue

查看 ionic-team/capacitor-plugins 的全部 Issue

相似的 Issue

更多 Java Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。