Swift中为何两次调用addSublayer实现iOS截屏拦截?
我发现一个用于iOS平台拦截截屏的UIWindow扩展方法,但不解为何要两次调用addSublayer。相关代码如下:
self.layer.superlayer?.addSublayer(textfield.layer) textfield.layer.sublayers?.last?.addSublayer(self.layer)
希望了解这两行代码的含义及该实现的工作原理。
两行代码的含义
第一行
self.layer.superlayer?.addSublayer(textfield.layer):
这里的self是UIWindow实例,self.layer.superlayer指向系统管理的窗口根容器层(UIApplication的主窗口容器)。这行代码将textfield的图层从原视图层级中剥离,直接挂载到系统窗口的根层级上,让它脱离原视图的约束。第二行
textfield.layer.sublayers?.last?.addSublayer(self.layer):
将当前UIWindow的内容图层(也就是原本显示App内容的图层)添加到textfield图层的最后一个子图层中,把原窗口内容变成textfield图层的子层级。
拦截截屏的工作原理
iOS系统截屏时会自动忽略UITextField的图层内容(出于隐私保护,系统默认不捕获输入框的敏感内容)。这两行代码通过重构图层层级,实现了截屏拦截:
最终的图层层级变为:系统根层 → textfield.layer → 原window.layer
此时原窗口的内容虽然能正常显示在屏幕上,但它属于textfield图层的子层级。当系统触发截屏时,会跳过整个textfield的图层树,自然也就不会捕获到原窗口的内容,从而达到拦截截屏的效果。
为什么要分两次调用addSublayer?
如果直接将原window图层挂到textfield图层再添加到根层,可能会因为textfield自身的视图属性(比如默认的层级渲染规则)导致原窗口内容无法正常显示。分两步操作既能让textfield图层脱离原视图层级,又能保证原窗口内容作为子图层正常显示,同时被系统截屏逻辑识别为需要忽略的内容。
内容的提问来源于stack exchange,提问作者JimmyLee

