はじめに
先日、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はスクショだけになりますが、実機で録画時も非表示になることは確認したのでどちらのパターンも動作します。
撮影後に表示されている文字列は階層を分けて後ろに仕込んでいます。
撮影禁止処理を有効にした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()
を呼び出すだけです。
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の動きがおかしくなる可能性もありますので、導入する際の検証はしっかり行った方が良いと思います。
おわりに
最初に実装方法だけみてなるほどと思いつつ、すごい解決方法だな(回答者の方も悩んだのかな)という印象でしたが、要件を満たせる上に実装も単純なので助けられました。