LoginSignup
15
9

SwiftUIで、スクショ・録画防止機能を作る

Last updated at Posted at 2024-04-02

背景

よりセキュアな設計にするために、スクショ・録画の防止機能を実装したい

課題1

  1. SwiftUIの標準APIにはこの機能がない
  2. NotificationCenterによる通知は、事後なのでスクショを防ぐことができない

方針

UITextFieldのisSecureTextEntryを応用し、シンプルなインターフェースを設計することにした

課題2

  1. ビューを消す方法は見つかるが、差し替える方法が見当たらない

「ぱっしょんぱっしょん〜(おまじない)」

定義側

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")
}

:warning: 注意点

  1. シミュレータ上でのcommand + sは、スクショ扱いにならないのでモザイクがかからない。
    Device > Trigger Screen Shot からスクショをすること。
  2. 録画中はモザイクがかかっていないが、写真フォルダに保存された動画にはモザイクがかかっている

まとめ

クソシンプルなインターフェースの、スクショ防止機能が出来上がりました。
ビューの差し替えや影響範囲も自由にカスタマイズ可能なので、ぜひ取り入れてみてください。

15
9
0

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
15
9