背景
よりセキュアな設計にするために、スクショ・録画の防止機能を実装したい
課題1
- SwiftUIの標準APIにはこの機能がない
- NotificationCenterによる通知は、事後なのでスクショを防ぐことができない
方針
UITextFieldのisSecureTextEntry
を応用し、シンプルなインターフェースを設計することにした
課題2
- ビューを消す方法は見つかるが、差し替える方法が見当たらない
「ぱっしょんぱっしょん〜(おまじない)」
定義側
import SwiftUI
// マルチモジュール想定なのでpublicにしてます
public struct ScreenshotBlockView<Content: View>: View {
let content: Content
let blurRadius: CGFloat
public init(blurRadius: CGFloat = 15, @ViewBuilder content: @escaping () -> Content) {
self.content = content()
self.blurRadius = blurRadius
}
public var body: some View {
_ScreenshotBlockHelper(content: content)
.background {
content.blur(radius: blurRadius) // 👈🏻 スクショ・録画時に差し替えられるビュー
}
.clipped()
.id(UUID().uuidString) // 👈🏻 スクロールビューで利用するならつけておかないと再描画されなかったりする
}
}
private struct _ScreenshotBlockHelper<Content: View>: UIViewRepresentable {
let content: Content
func makeUIView(context: Context) -> some UIView {
let secureField = UITextField()
secureField.isSecureTextEntry = true
if let textLayoutView = secureField.subviews.first {
return textLayoutView
}
return UIView()
}
func updateUIView(_ uiView: UIViewType, context: Context) {
let hostingView: _UIHostingView<Content> = .init(rootView: content)
hostingView.frame = uiView.bounds
hostingView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
uiView.addSubview(hostingView)
// iOS15系で表示崩れの原因になるので、👆🏻を使った方が良さそう
// let hostingController = UIHostingController(rootView: content)
// hostingController.view.translatesAutoresizingMaskIntoConstraints = false
// uiView.addSubview(hostingController.view) // 👈🏻 スクショ・録画をしていない時に表示するビュー
// NSLayoutConstraint.activate([
// hostingController.view.topAnchor.constraint(equalTo: uiView.topAnchor),
// hostingController.view.bottomAnchor.constraint(equalTo: uiView.bottomAnchor),
// hostingController.view.trailingAnchor.constraint(equalTo: uiView.trailingAnchor),
// hostingController.view.leadingAnchor.constraint(equalTo: uiView.leadingAnchor)
// ])
}
}
利用側
ScreenshotBlockView { // 👈🏻 このブロック内のビューが、差し替えられる
Image("userIcon")
}
注意点
- シミュレータ上での
command + s
は、スクショ扱いにならないのでモザイクがかからない。
Device > Trigger Screen Shot からスクショをすること。 - 録画中はモザイクがかかっていないが、写真フォルダに保存された動画にはモザイクがかかっている
まとめ
クソシンプルなインターフェースの、スクショ防止機能が出来上がりました。
ビューの差し替えや影響範囲も自由にカスタマイズ可能なので、ぜひ取り入れてみてください。