# 04.iOS截屏防护方案

### 前言

想必很多同学都有遇到过或者想过怎么能防止系统截图，但通过大量的搜索并没有找到具体的实现方案。

### 一、 常见方案

#### 1.1 系统通知

`UIApplication.userDidTakeScreenshotNotification` 这个系统通知是在完成截屏动作后，系统给到 `App` 的，在收到这个通知后做处理，并不能达到防护的效果。

微信的付款吗就是这样的实现，在截屏后提供一个警示内容。

![微信](/files/DX6ikPpvThxGkvzc0B4U)

#### 1.2 小红书

在使用小红书的截屏的时候顶部状态栏会添加一个小红书的水印如下图：

![小红书](/files/WH3XwlLa2QwOgYIBsRPf)

它是怎么实现的呢？我有两个猜测：

**第一种：水印一直在，刘海后面**

因为非刘海屏没有这个水印，所以估计是这样，但是通过切换到任务管理器状态并没有看到有那个水印。

![小红书](/files/HA1sZlEs0rSb9ELoRjI4)

**第二种：通过什么黑科技加上的？**

不是第一种，难道真的是又什么黑科技？

**继续验证**

我觉得肯定是第一种，觉得任务切换的状态下是不是那个水印被系统隐藏掉。想道还有 QuickTime 可以投屏来验证。

![小红书](/files/OwjQNTrXJppGMYOKqOio)

破案，果然是藏在刘海后面的，没有继续深入的价值了🤷‍♂️。

#### 1.3 ScreenShieldKit

![ScreenShieldKit](/files/S3HMRFEfZ3AM2YwLToHE)

在寻找解决方案的时候发现了这样一个SDK [ScreenShieldKit](https://screenshieldkit.com)

> Contact us for more information and to receive a free evaluation SDK!
>
> 这句话一看就是收钱的了，抱着能白嫖绝不三连的宗旨，先略过暂不深入研究。（开玩笑，如果你觉得我的方案不错，本文求赞评转+Star！）

#### 1.4 爱奇艺

爱奇艺使用的是截图后直接读取用户相册删除图片的方式进行的，不给权限，截图就不会被删除。

![爱奇艺](https://github.com/RyukieSama/RyukieDevGitBook/blob/master/iOS/Optimize/Image/04-09.JPG)

#### 1.5 UITextField

当我们将一个 `UITextField` 的 `isSecureTextEntry` 设置为 `true` 的时候，会隐去输入的文案用 `圆点` 替代。并且在进行录屏或者截屏的时候都会被系统隐去。

下面用我的个人项目 [梦见账本](https://apps.apple.com/cn/app/id1498426607)

如下图我正在用 `QuickTime` 进行录屏：

![UITextField](/files/94J7qKeEyUkSGB7ufYbx)

那么我们是否可以使用这种特性呢？

### 二、 RyukieSwifty/ScreenShield

基于 `UITextField` 的效果我实现了一个 `ScreenShieldView` 可以很方便的进行使用。

[GitHub: RyukieSwifty/ScreenShield](https://github.com/RyukieSama/Swifty)

***如果觉得不错的话，欢迎留个⭐️哦***

#### 2.1 使用方式

**Cocoapods 导入**

```ruby
pod 'RyukieSwifty/ScreenShield'
```

**使用**

![RyukieSwifty/ScreenShield](/files/uk8GqEB53CnpNkma5OtP)

例如对整个控制器的 `View` 进行截屏防护：

```
import UIKit
import RyukieSwifty

class TransactionAddViewController: UIViewController {
    // MARK: - Life
    override func loadView() {
        view = ScreenShieldView.create()
    }
    ...
}
```

#### 2.2 如何实现的

![8](/files/tPUBXoQwkf9qU97fxYdT)

按照向里面添加子视图的方式验证具体的原因，最后发现这个效果是由一个私有类实现的 `_UITextLayoutCanvasView` 。他本质上也是一个 `UIView` ，所以理论上只要我们能够创建出来一个它，那么就可以将想要保护的内容添加进去。

由于它是私有类，无法直接创建，并且如果直接通过字符串区创建也担心有审核风险，于是我通过下面的方式来创建。

```
private func makeSecureView() -> UIView? {
    let field = UITextField()
    field.isSecureTextEntry = true
    let fv = field.subviews.first
    fv?.subviews.forEach { $0.removeFromSuperview() }
    fv?.isUserInteractionEnabled = true
    return fv
}
```

> 当前手头没有更多的系统版本设备可以测试，已经验证的 `13.7 ～ 15.3.1` 是没问题的。
>
> 不排除会存在某些版本上 `UITextField` 的结构发生了调整导致失效的问题，如果有，欢迎在 [GitHub](https://github.com/RyukieSama/Swifty) 提 `issue` 。

### 总结

不知道各位有没有其他的实现方式，有的话欢迎一起交流。

如果本文有帮到你，欢迎赞评转+Star！


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ryukiedev.gitbook.io/wiki/ios/you-hua/04.ios-jie-ping-fang-hu-fang-an.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
