You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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方法做了错误的图层篡改操作:

  1. 将WebView的图层(self.layer)添加到文本框的子图层中,直接破坏了WebView的渲染层级,导致恢复激活时WebView无法正常显示。
  2. 重复添加图层(self.layer.superlayer?.addSublayer(field.layer))进一步打乱了视图结构。
  3. 移除文本框时仅调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 00:01:06