28
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

iOSのスクリーンショットでコンテンツを消す方法

Last updated at Posted at 2022-02-06

はじめに

先日、PMからiOSアプリで動画配信画面のスクリーンショットや画面録画で撮られたときに
動画コンテンツの部分をNetflixやAmazon Prime Videoみたいに黒塗りにしたいけどできるかな?と相談を受けました。

スクショ対策はAndroidはWindowManager.LayoutParams.FLAG_SECUREを付けておくだけで上手くやってくれるのですが、iOSはUIApplication.userDidTakeScreenshotNotificationで撮影後にファイルを削除する(これもできるのか不明)などの情報ばかりでした。

そんな中、StackOverFlowに投稿されていた回答を試したところ動作するOSバージョンに制限はあったものの期待通りの動きになったので、その検証結果の記事になります。

検証といってもそのまま試しただけですが、探しても成功事例の情報が少なかったので情報を流して、今後似たような対応をする方がいれば、その方の参考になればいいなと思います。

元ネタの回答はこちらです。
Prevent screen capture in an iOS app

試した結果

画面は上がUIImageView, 下がAVPlayerになっています。
実機でスクショや画面録画しても何も映らないのでgifは Simulator を画面録画したものになります。
gifはスクショだけになりますが、実機で録画時も非表示になることは確認したのでどちらのパターンも動作します。

take_screen.gif

撮影後に表示されている文字列は階層を分けて後ろに仕込んでいます。
撮影禁止処理を有効にしたUIViewの領域がまるっと非表示になります。

動作確認について

すぐに手元で検証できる実機が iOS15 以上だけだったので以下は Simulator で検証しました。
スクショの撮影は Simulator メニューのDevice -> Trigger Screenshotで撮影した場合に実機と同じようになります。

元の回答には iOS13 以降で動作するというコメントがありますが iOS13 でも動作するバージョンとそうでないバージョンはありそうなのでサポートするなら確認は必要になります。

OSバージョン 結果
15.x
14.0
13.4
13.0 ×
12.4 ×

あと Simulator で検証していると AVPlayer でエラーが出ていて動かない問題がありましたが、iOS13.4 は AVPlayer のエラーが出ていても機能していたので、その辺りは関係ないと判断しています。

実装

撮影させたくない画像や動画をUIViewの入れ子にして
そのUIViewのレイヤーにisSecureTextEntryを有効にしたUITextFieldを加えるという方法で実現していました。

あとは対象の領域を囲ったUIViewでmakeSecure()を呼び出すだけです。

.swift
import UIKit

extension UIView {
    func makeSecure() {
        DispatchQueue.main.async {
            let field = UITextField()
            field.isSecureTextEntry = true
            self.addSubview(field)
            field.translatesAutoresizingMaskIntoConstraints = false
            field.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
            field.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
            self.layer.superlayer?.addSublayer(field.layer)
            field.layer.sublayers?.first?.addSublayer(self.layer)
        }
    }
}

なぜこれが機能するのかという回答者からの問いかけにはコメントでUITextFieldの内部の_UITextLayoutCanvasViewが表示を隠す為に機能しているということでしたが、この辺りは詳しくないのでまた時間があるときに調べてみます。

gifのプロジェクトはこちらです。
GitHub mittsu333/SecureScreen

注意点: 実装の弊害

OSバージョンで動作する・しない以外にもUITextFieldをレイヤーに差し込むことで画面の作りによっては思わぬ弊害を生む可能性もあると思います。

動画プレイヤーなどでレイヤー周りを操作している場合や、既存で配置している他のUITextFieldの動きがおかしくなる可能性もありますので、導入する際の検証はしっかり行った方が良いと思います。

おわりに

最初に実装方法だけみてなるほどと思いつつ、すごい解決方法だな(回答者の方も悩んだのかな)という印象でしたが、要件を満たせる上に実装も単純なので助けられました。

28
13
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?