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

UIWindow防录屏/截屏实现中的循环引用及动态取消安全状态黑屏问题解决方案咨询

UIWindow防录屏/截屏实现中的循环引用及动态取消安全状态黑屏问题解决方案咨询

嘿,我来帮你搞定这两个棘手的问题!你用UITextField的secure特性实现防录屏/截屏的思路其实挺巧妙的,但原代码里的层级嵌套确实踩了两个大坑——循环引用和取消时黑屏,我给你梳理下问题根源和修复方案:

一、循环引用的问题根源与修复

原代码里这两行是罪魁祸首:

self.layer.superlayer?.addSublayer(field.layer)
field.layer.sublayers?.last?.addSublayer(self.layer)

你把window的layer嵌套到了UITextField的私有secure层下面,同时UITextField又是window的子视图,它的layer原本就依赖window的layer作为父层,这就形成了循环引用链:window的layer的父层是field的layer的子层,而field的layer的父层又是window的layer——这种互相嵌套的层级会导致内存无法正常释放,轻则内存泄漏,重则直接崩溃。

修复思路很简单:不要嵌套window的layer,我们只需要把UITextField的那个私有secure层单独抽出来,放到window的layer的同级层级(也就是window所在场景的根layer),让它覆盖整个window即可。这样既利用了secure层的防录屏特性,又不会形成循环引用。

二、动态取消安全状态时黑屏的问题修复

原代码里移除UITextField时黑屏,是因为你之前把window的layer嵌套到了field的layer下面,移除field的时候,它的整个layer树都会被销毁,window的layer也跟着被带走了,自然就黑屏了。

修复思路:我们根本不需要把UITextField添加为window的子视图,也不需要嵌套window的layer——只需要提取它的secure层,单独管理这个层的生命周期。取消安全状态时,只需要移除这个secure层就行,window的layer完全不受影响。

完整修复后的代码

import UIKit

private var secureLayerKey: UInt8 = 0

extension UIWindow {
    // 用关联对象保存secure层的引用,方便后续移除
    private var secureOverlayLayer: CALayer? {
        get {
            return objc_getAssociatedObject(self, &secureLayerKey) as? CALayer
        }
        set {
            objc_setAssociatedObject(self, &secureLayerKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    func makeSecure() {
        // 避免重复添加secure层
        guard secureOverlayLayer == nil else { return }
        
        // 只创建UITextField来获取它的secure层,不需要添加到视图层级
        let secureField = UITextField()
        secureField.isSecureTextEntry = true
        secureField.bounds = self.bounds
        
        // 提取UITextField的私有secure层(就是它layer的最后一个子层)
        guard let secureSublayer = secureField.layer.sublayers?.last else { return }
        
        // 设置secure层覆盖整个window,关闭交互避免挡住用户操作
        secureSublayer.frame = self.bounds
        secureSublayer.isUserInteractionEnabled = false
        
        // 把secure层添加到window的父层级(场景根layer),和window的layer同级
        self.layer.superlayer?.addSublayer(secureSublayer)
        
        // 保存引用,后面取消安全状态时要用
        self.secureOverlayLayer = secureSublayer
    }

    func makeUnsecure() {
        // 移除之前添加的secure层,恢复正常状态
        guard let secureLayer = secureOverlayLayer else { return }
        secureLayer.removeFromSuperlayer()
        secureOverlayLayer = nil
    }
}

这个方案为啥能满足你的需求?

  • 彻底解决循环引用:所有layer都是单向层级,没有互相嵌套的情况,内存可以正常释放
  • 不需要动态隐藏/显示视图:secure层一直存在,正常状态下完全不影响用户操作(关闭了交互,无内容时是透明的),只有在录屏/截屏时会自动触发模糊效果
  • 取消安全状态不会黑屏:移除的只是单独的secure层,window的layer完全不受影响,自然不会出现黑屏

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 12:14:29