iOS 全局防截屏原理
一、系统背景
- iOS 对「安全输入」相关视图(例如开启安全输入的文本框及其内部系统私有子视图)在截屏与录屏时会自动排除,这些视图不会出现在截图或录屏画面中。
- 项目利用这一机制,将整屏应用内容放入系统认定的「安全视图」层级内,从而实现防截屏/防录屏下的内容保护。
二、实现原理概览
2.1 安全容器从何而来
- 使用一个透明的、开启安全输入的文本框(系统会为其创建内部私有子视图用于安全渲染)。
- 取该文本框的第一个子视图(即系统为安全输入创建的内容视图)作为「安全容器」。
- 将该安全容器从文本框上移除并清空其原有子视图,再作为窗口的唯一直接子视图添加到窗口上,设全屏 frame 与自动布局,使窗口的直接子视图只有一个:安全容器本身。
示意代码(Swift):
1 | // 安全窗口内:构建安全容器并挂到窗口上 |
2.2 内容如何进入安全容器
- 重写窗口的 addSubview:除「安全容器自身」外,所有通过该窗口添加的视图都不再加在窗口上,而是统一加到安全容器内。
- 这样,无论是根界面还是后续加在「窗口」上的浮层(弹窗、蒙层、设置面板等),最终都在安全容器内,从而在截屏/录屏时被系统整体排除。
示意代码(Swift):
1 | // 安全窗口:重写 addSubview,将内容重定向到安全容器 |
2.3 根内容与浮层的区别对待
- 根内容:第一个被加入安全容器的视图(通常为根控制器的 view)。对其单独处理:设置
frame = 安全容器.bounds并设置自动调整大小,保证全屏且随窗口变化。 - 后续浮层:不再改 frame,保持调用方传入的 frame,避免把弹窗、小视图等误设为全屏。
2.4 文本框的保留
- 安全容器来自文本框的内部子视图,文本框本身不再挂在视图层级上,但需在窗口侧强引用保留该文本框,避免在部分系统版本上被释放导致异常。
三、注意事项(原理层面)
3.1 按 tag 查找「加在窗口上的浮层」
- 现象:在启用安全窗口后,窗口的直接子视图只有安全容器这一项,原先加在「窗口」上的浮层实际都在安全容器的子视图里。
- 注意:凡是通过「遍历窗口的直接子视图」按 tag 查找浮层的逻辑,会找不到目标。
- 正确做法:若当前为安全窗口,应使用安全窗口提供的「用于 overlay 查找的子视图列表」(即安全容器内的子视图数组)进行遍历与按 tag 查找;非安全窗口时仍使用窗口的
subviews。这样无论是普通窗口还是安全窗口,都能正确找到浮层并执行移除、拖拽等逻辑。
示意代码(Objective-C):
1 | // 按 tag 查找加在「窗口」上的浮层时,兼容安全窗口 |
3.2 Toast、Loading 等「以窗口为父视图」的展示
- 现象:安全容器对应的系统私有视图,对后加在其上的部分子视图可能存在渲染或命中测试上的差异,导致 Toast、Loading 等加在窗口上时显示异常或尺寸被错误修改(若曾对「所有加在窗口的视图」统一设全屏 frame,会加剧该问题)。
- 注意:若 Toast / Loading 在「未指定父视图」时默认加在窗口上,在安全窗口下可改为加在顶层控制器视图等稳定容器上,避免依赖安全容器对后加子视图的渲染行为;同时仅对「根内容」设全屏 frame,不对后续 overlay 改 frame。
示意代码(选择展示容器):
1 | // 当 inView 为空时,决定 Toast/Loading 加在哪个视图上 |
3.3 浮层点击、拖拽「不响应」
- 可能原因一:事件与查找无关,而是查找失败。拖拽/点击的处理逻辑里若仍通过「窗口的直接子视图」按 tag 查找浮层,在安全窗口下会找不到,逻辑提前 return,表现为「不响应」。
- 可能原因二:安全容器为系统私有视图,其子视图的 hitTest / 事件传递在个别场景下可能与预期不符,需结合具体视图层级与手势配置排查。
- 建议:先统一将「在窗口上按 tag 找浮层」改为使用上述 overlay 查找方式,再视情况排查 hitTest、手势冲突、
userInteractionEnabled等。
3.4 坐标转换、只读使用窗口
- 仅读取窗口、做坐标转换(如
convertRect:fromView:)、访问rootViewController等与 addSubview 规则无关的用法,无需因防截屏而修改。
3.5 防截屏的边界
- 防截屏仅影响被放入安全容器的内容在截屏/录屏画面中的可见性(被系统排除)。
- 应用内仍可监听系统截屏/录屏通知(如
UIScreenCapturedDidChangeNotification、UIScreen.isCaptured)做业务逻辑:例如提示用户、显示自定义遮挡层等,与「安全容器内不参与截屏」是互补关系。
四、小结
| 要点 | 说明 |
|---|---|
| 核心思路 | 用系统「安全输入」视图的私有子视图作为安全容器,重写窗口 addSubview 使所有内容进该容器,从而被截屏/录屏排除。 |
| 根内容 | 仅第一个加入安全容器的视图设全屏 frame;其余 overlay 保持原 frame。 |
| 查找浮层 | 安全窗口下必须用「overlay 查找接口」返回的列表(安全容器子视图),不能再用窗口的直接 subviews。 |
| Toast/Loading | 可考虑以顶层 VC.view 等为展示容器,避免安全容器对后加子视图的渲染/尺寸问题。 |
| 不响应交互 | 先检查是否因「仍用窗口 subviews 查找」导致找不到视图;再排查 hitTest、手势与层级。 |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 今是昨非的博客!