How to prevent screen recording and screenshots in SwiftUI and UIKit


Greetings, traveler!

Sometimes, we must prevent users from screen recording or taking screenshots. This is not a big deal for both UIKit and SwiftUI. Let’s delve into this topic a bit.

UIView extension

Spoiler: we will use the UIKit tools to create both solutions. So, it’s time to write an extension for UIView.

The key tool will be a UITextField, which can hide its content from capturing. You can toggle the isSecureTextEntry property value to enable this functionality. So, let’s create a static computed property for UIView, where we will extract this secure view from UITextField and return its value.

extension UIView {
    
    static var secureView: UIView {
        let textField = UITextField()
        textField.isSecureTextEntry = true
        textField.isUserInteractionEnabled = false
        guard let secureView = textField.layer.sublayers?.first?.delegate as? UIView else {
            return .init()
        }
        
        secureView.subviews.forEach { subview in
            subview.removeFromSuperview()
        }
        
        return secureView
    }
    
}

Ok then, we can move to the SwiftUI solution.

SwiftUI

To use UIKit UIView in the SwiftUI View, we can create a UIViewRepresentable instance. Here, we will provide a parent View via initializer.

struct RestrictCaptureView<Content: View>: UIViewRepresentable {
    
    private let content: () -> Content
    
    init(@ViewBuilder content: @escaping () -> Content) {
        self.content = content
    }
    
}

In the makeUIView method, we will create a UIHostingController with our parent View as its root view. Then, we will put this hosting controller’s view inside the secure view instance.

struct RestrictCaptureView<Content: View>: UIViewRepresentable {
    
    private let content: () -> Content
    
    init(@ViewBuilder content: @escaping () -> Content) {
        self.content = content
    }
    
    func makeUIView(context: Context) -> UIView {
        let secureView: UIView = .secureView
        
        let hostingController = UIHostingController(rootView: content())
        hostingController.view.backgroundColor = .clear
        hostingController.view.translatesAutoresizingMaskIntoConstraints = false
        
        secureView.addSubview(hostingController.view)
        NSLayoutConstraint.activate([
            hostingController.view.topAnchor.constraint(equalTo: secureView.topAnchor),
            hostingController.view.bottomAnchor.constraint(equalTo: secureView.bottomAnchor),
            hostingController.view.leadingAnchor.constraint(equalTo: secureView.leadingAnchor),
            hostingController.view.trailingAnchor.constraint(equalTo: secureView.trailingAnchor)
        ])
        
        return secureView
    }
    
    func updateUIView(_ uiView: UIView, context: Context) {}
}

Now, we can create such an extension for View.

extension View {

    @ViewBuilder
    func restrictCapture() -> some View {
        RestrictCaptureView { self }
    }
    
}

And use it.

struct ContentView: View {
    
    var body: some View {
        Text("Data to hide")
            .ignoresSafeArea()
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .restrictCapture()
    }
    
}

UIKit

We are finished with the SwiftUI solution. Now, let’s examine how we can do this with UIKit.

To secure the viewcontroller’s content, we can make its view secure. You can do this in the loadView method or just use your secure view as viewcontroller’s view subview. Here, we will use the first option.

final class ViewController: UIViewController {
    
    private let rootView: UIView = .init()
    
    override func loadView() {
        view = .secureView
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        rootView.backgroundColor = .red
        
        view.addSubview(rootView)
        rootView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            rootView.topAnchor.constraint(equalTo: view.topAnchor),
            rootView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            rootView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            rootView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])
    }
    
}

Conclusion

That’s it! We learned how to hide content from screen recording and prevent screenshots. Sure, the user can just take a photo or video via another smartphone. But there is no legal way to prevent such things.