Cordova防截图插件添加UITextField,后台激活后屏幕空白问题排查
Cordova防截图插件恢复激活后空白问题排查与解决
问题描述
我给Cordova应用开发了一个防截图插件,采用添加带isSecureTextEntry属性的UITextField方案。应用从杀死状态启动时功能正常,但从后台恢复激活后,屏幕显示空白(白色)界面。
我猜测是文本框覆盖了WebView导致的,于是尝试在应用退到后台时调用removeTextField()移除文本框,但完全无效。请问如何解决恢复激活时的空白问题?
插件代码
class WebExPlugin : CDVPlugin { var isEnabled = false override func pluginInitialize() { enable(completion: nil) } NotificationCenter.default.addObserver(self, selector: #selector(self.handleWillResignActiveNotification), name: UIApplication.willResignActiveNotification, object: nil) @objc public func enable(completion: (() -> Void)?) { self.isEnabled = true DispatchQueue.main.async { self.webView.addSecureTextField() completion?() } } @objc public func disable(completion: (() -> Void)?) { self.isEnabled = false DispatchQueue.main.async { self.webView.removeTextField() completion?() } } // 测试用:退到后台时禁用 @objc public func handleWillResignActiveNotification() { if self.isEnabled { DispatchQueue.main.async { // 根据建议改为superview调用 self.webView?.superview!.removeTextField() } } } } public extension UIView { func addSecureTextField() { let field = UITextField() field.isSecureTextEntry = true field.translatesAutoresizingMaskIntoConstraints = false field.tag = 101; self.addSubview(field) field.centerYAnchor.constraint(equalTo: self.topAnchor).isActive = true field.centerXAnchor.constraint(equalTo: self.trailingAnchor).isActive = true self.layer.superlayer?.addSublayer(field.layer) field.layer.sublayers?.last?.addSublayer(self.layer) } // 无法生效! func removeTextField() { // 根据@T.Pasichnyk建议更新代码 self.viewWithTag(101)?.removeFromSuperview() } }
补充信息
视图调试器中的视图层级:
问题根源&修复方案
核心问题
你的addSecureTextField方法做了错误的图层篡改操作:
- 将WebView的图层(
self.layer)添加到文本框的子图层中,直接破坏了WebView的渲染层级,导致恢复激活时WebView无法正常显示。 - 重复添加图层(
self.layer.superlayer?.addSublayer(field.layer))进一步打乱了视图结构。 - 移除文本框时仅调用
removeFromSuperview(),但未清理错误的图层关联。
修复步骤
1. 重构文本框添加/移除方法
不需要调整图层层级,iOS的安全文本框只要存在于视图树中,就会触发系统防截图机制。我们只需将文本框放在不影响视觉的位置即可:
public extension UIView { func addSecureTextField() { // 避免重复添加 guard viewWithTag(101) == nil else { return } let field = UITextField() field.isSecureTextEntry = true field.translatesAutoresizingMaskIntoConstraints = false field.tag = 101 // 将文本框插入到视图层级最底部,完全不遮挡WebView self.insertSubview(field, at: 0) // 把文本框定位到屏幕外,不影响界面显示 field.topAnchor.constraint(equalTo: self.topAnchor, constant: -100).isActive = true field.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true field.widthAnchor.constraint(equalToConstant: 1).isActive = true field.heightAnchor.constraint(equalToConstant: 1).isActive = true } func removeTextField() { viewWithTag(101)?.removeFromSuperview() } }
2. 修正插件生命周期逻辑
添加应用激活的监听,在恢复时重新初始化文本框,同时修正后台移除的逻辑:
class WebExPlugin : CDVPlugin { var isEnabled = false override func pluginInitialize() { let nc = NotificationCenter.default nc.addObserver(self, selector: #selector(handleWillResignActiveNotification), name: UIApplication.willResignActiveNotification, object: nil) nc.addObserver(self, selector: #selector(handleDidBecomeActiveNotification), name: UIApplication.didBecomeActiveNotification, object: nil) enable(completion: nil) } @objc public func enable(completion: (() -> Void)?) { self.isEnabled = true DispatchQueue.main.async { self.webView.addSecureTextField() completion?() } } @objc public func disable(completion: (() -> Void)?) { self.isEnabled = false DispatchQueue.main.async { self.webView.removeTextField() completion?() } } @objc func handleWillResignActiveNotification() { if isEnabled { DispatchQueue.main.async { self.webView.removeTextField() } } } @objc func handleDidBecomeActiveNotification() { if isEnabled { DispatchQueue.main.async { self.webView.addSecureTextField() } } } // 插件销毁时移除监听,避免内存泄漏 deinit { NotificationCenter.default.removeObserver(self) } }
关键说明
- 安全文本框不需要覆盖WebView,只要存在于视图层级中就能触发防截图,所以放在屏幕外或层级底部完全不影响功能。
- 每次应用激活时重新添加文本框,避免后台恢复时的层级异常。
- 移除所有错误的图层操作,保证WebView的渲染结构正常。
内容的提问来源于stack exchange,提问作者Andreas
相关产品推荐
相关产品推荐

