在iOS应用里使用WKWebView加载含HTML5视频的页面时,用户开启视频画中画(Picture in Picture)后,网页中的输入框如果自动获得焦点,系统键盘会照常弹出。但由于画中画窗口是独立于App主窗口的悬浮层,原网页可能已经被最小化或覆盖,此时输入框虽然逻辑上拥有焦点,视觉上却不在用户可见区域,造成“键盘弹了但不知道输到哪”的困惑。这种焦点与视图状态不一致的问题,在直播弹幕、视频评论等场景中尤为突出。

画中画模式下焦点错位的底层原理
iOS的UIResponder链决定了哪个控件能接收键盘事件。WKWebView作为容器,其内部的网页输入框通过WebKit桥接成为间接的第一响应者。当视频进入画中画,AVPictureInPictureController会让视频窗口脱离原有的UIWindow层级,而WKWebView所在页面通常仍保留在后台窗口中。此时网页JS调用focus()方法,依然会把输入框标记为active,但系统键盘依附于当前最前端的application window,用户视线却在画中画小窗,两者空间分离。
另一个容易被忽略的点是,WKWebView的content view在画中画开启后并不会自动暂停JS执行,因此页面里的自动聚焦脚本(例如播放后延迟触发输入框focus)仍会运行。开发者若未在原生层拦截,就会出现在悬浮窗播放时突然弹出键盘的突兀体验。理解这个机制,才能知道为什么单纯在网页里写blur()不一定生效,因为原生端的响应者状态可能未被同步清除。
从WebKit源码角度看,输入框的焦点状态由WebProcess管理,而键盘由UIProcess的UIKit接管。跨进程通信存在时延,当画中画状态变化通知从AVKit回传到WKWebView时,若网页已自行完成focus,原生层收到的回调往往滞后。这也是为何很多团队反馈“监听了画中画代理却拦不住键盘”的根本原因:拦截点选错了,应该在Web侧配合原生侧双重控制。
常见处理方案与优缺点对比
目前社区里主要有两种思路。第一种是禁止画中画期间网页获焦:在原生层监听AVPictureInPictureController的willStartPictureInPicture和didStopPictureInPicture,通过WKWebView的evaluateJavaScript向页面注入一个全局锁,阻止输入框调用focus。这种方案实现简单,能彻底避免键盘弹出,但代价是用户无法在画中画时发评论,对互动类App不够友好。
第二种方案是允许聚焦但做视图补偿:当检测到画中画开启,原生端将WKWebView所在控制器以半透明形式保留在画中画窗口附近,或是在画中画窗口旁用原生UIView叠加一个输入框,把网页焦点转移到原生框,再桥接内容回网页。该方式体验完整,但开发量大,且要处理原生与网页双向文本同步。下面的代码演示了如何用代理控制全局锁:
import AVKit
import WebKit
class PlayerCoordinator: NSObject, AVPictureInPictureControllerDelegate {
var webView: WKWebView?
func pictureInPictureControllerWillStartPictureInPicture(_ controller: AVPictureInPictureController) {
let script = "window.__disableFocus = true;"
webView?.evaluateJavaScript(script, completionHandler: nil)
}
func pictureInPictureControllerDidStopPictureInPicture(_ controller: AVPictureInPictureController) {
let script = "window.__disableFocus = false;"
webView?.evaluateJavaScript(script, completionHandler: nil)
}
}
对比来看,若产品定位是纯播放工具,第一种方案性价比最高;若强调社区互动,则必须采用第二种思路的变体。注意无论哪种方案,都不应在原生端直接调用输入框的resignFirstResponder来粗暴关键盘,因为WKWebView的内部响应者并不暴露给UIKit,强制调用可能引发白屏或输入线程卡死。
实战中的输入框焦点管理策略
在具体编码时,建议网页侧增加一个焦点守卫。以下JS代码在画中画锁开启时拦截focus调用,确保不会无故弹键盘:
(function() {
window.__disableFocus = false;
const originalFocus = HTMLElement.prototype.focus;
HTMLElement.prototype.focus = function() {
if (window.__disableFocus && this.tagName === 'INPUT' || this.tagName === 'TEXTAREA') {
return;
}
return originalFocus.apply(this, arguments);
};
})();
原生层除了设置锁,还应监听键盘框架变化。当画中画开启且锁生效时,如果系统仍弹键盘,可通过NotificationCenter观察UIKeyboardWillShowNotification并主动隐藏。但更稳妥的做法是在willStartPictureInPicture里先对WKWebView执行becomeFirstResponder的取消逻辑,再注入锁脚本,形成双重保险。
对于需要边看边输入的场景,可以在画中画启动后,由原生弹出一个UIAlertController带输入框,用户输入完成再evaluateJavaScript把值写入网页评论框,并触发提交事件。这样输入框焦点始终在原生层,不会与画中画窗口冲突。代码示例如下:
func showFloatingInput() {
let alert = UIAlertController(title: "发弹幕", message: nil, preferredStyle: .alert)
alert.addTextField { $0.placeholder = "说点什么" }
alert.addAction(UIAlertAction(title: "发送", style: .default, handler: { _ in
if let text = alert.textFields?.first?.text {
let js = "document.getElementById('comment').value = '(text)';"
self.webView?.evaluateJavaScript(js, completionHandler: nil)
}
}))
UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: true, completion: nil)
}
最后提醒,测试时务必覆盖iOS 15到最新系统的行为差异。新系统对画中画窗口的焦点策略略有收紧,部分版本中WKWebView在画中画下会直接忽略focus请求,此时网页守卫反而可去掉以兼容。把握“原生控状态、网页控行为”的原则,就能在各类iOS端平稳解决键盘与输入框焦点管理的矛盾。
WKWebViewpicture_in_picturekeyboard_focus修改时间:2026-08-18 07:44:33