Prevent screen capture in an iOS app
Asked Answered
B

8

83

I need to prevent screen capture by users of my app, for security reasons. The contents I display are confidential and should not be copied onto the device. I saw one answer on Stack Overflow, but for Android.

Is it possible somehow in iOS to prevent screen capture?

While capturing the screenshot into the gallery by the click of few buttons is a very useful feature for the user, there is a limited requirement to prevent it too. Any pointers?

Bankhead answered 8/9, 2013 at 3:13 Comment(7)
It's an OS level action, so I don't think it would be possible, unless you're running on a jailbroken device. I could be wrong though.Tarrsus
I don't think you can prevent it on a non-jailbroken device, but you can detect it - #13485016Apulia
The other problem is screen capture in wetware - as in a person captures the screen with another device such as a camera or other phone. Even if you prevent it in the app it's impossible to prevent someone taking a photo of the screenChaldean
Is there any way to detect captured image in the block and we can make it obscured?Jez
I already saw some security frameworks who do that, but I don't know exactly what they do, but certainly some system call interception.Nino
Do you solve the issue?Monostylous
I created this ScreenShield Kit, an iOS library designed to secure content by blocking screenshots and screen recordings. It provides a simple solution to protect your app's content. I hope it proves helpful for others facing the same challenge.Jaggy
K
75

I've just wrote simple extension of UIView that allows to hide it from screen-capturing, Airplay mirroring and so on. The solution uses ability of UITextField to hide a password from capturing.

extension UIView {
    func makeSecure() {
        DispatchQueue.main.async {
            let field = UITextField()
            field.isSecureTextEntry = true
            field.isUserInteractionEnabled = false
            self.addSubview(field)
            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)
        }
    }
}

using:

class ViewController: UIViewController {

   var secureView: UIView!

   override func viewDidLoad() {
      super.viewDidLoad()

      secureView.makeSecure()
   }
}

I'll be grateful if someone explains how Apple does this magic inside.

Katerinekates answered 12/4, 2021 at 8:20 Comment(19)
It's a bit like magic, but the approach actually works! It messes with the autolayout though... any idea how to fix it?Anthracene
Also, what's your source on this? It's seemingly mentioned nowhere on the internetAnthracene
This is my own elaboration :) I also tried to find something similar on the internet, but couldn'tKaterinekates
It should also be noted that this will only work on iOS 13+Katerinekates
This can cause several issues and I recommend against it. If your layer also has other sublayers (a custom view, for example) then this will most possibly crash depending on your implementation.Breannebrear
I implement this on my react-native project, works great, thanks a lotEthyne
@Katerinekates _UITextLayoutCanvasView is responsible for hiding things (see gist.github.com/damian-rzeszot/bbfd92895dfe9400ba9a7cf2a0c1e670)Nimmons
@NaldDev sorry for necroposting but could you share your RN implementation? For some reason it does not work for me :(Vollmer
As @Breannebrear mentioned this can cause issues with a view that has other sublayers. I worked through a crash with PDFKit by applying the solution to the PagingController layer. If you get a crash, open the view hierarchy debugging tool and access ONLY the layer you want to secure, not the entire view.Marlie
Works like a charm in a normal controller. Hopefully I will be able to integrate it into a PDF reader.Meneses
Minimizing and opening back the app the view will be red in color. How can we toggle it back to its original state?Extort
@Katerinekates why is it working on iOS 13 and above. All the functionalities and members used are iOS 9 and above. Any specific reason.Yclept
@AnkitKumarGupta the deal is in UiTextField. It has a different behaviour in iOS 12 and below. It doesn’t hide password typing from screen capturing. So yes, my method is just a hack)Katerinekates
hi, can i write this code in objective-c?Seaton
Can anybody explain how it works?D
This hack still works!Myelitis
I'm not longer seeing this approach working on iOS 17. Has anyone identified an alternative approach?Seftton
What about SwiftUI? How do I apply this in a SwiftUI app?Caponize
field.layer.sublayers?.last?.addSublayer(self.layer) seems to work on iOS17Vespucci
P
32

There's no way to prevent taking screenshots entirely. You can do what Snapchat does, which is by requiring the user to be touching the screen to view whatever information you're displaying. This is because the system screenshot event interrupts touches. It's not a perfect method and you can't prevent users from taking screenshots 100% of the time.

More details: iOS Detection of Screenshot?

Psilocybin answered 8/9, 2013 at 4:45 Comment(6)
I don't use Snapchat so I can't test it right now, but What happens if you plug your iPhone to a Mac and take a screenshot through Xcode? (finger on the screen all along). I'm pretty sure there's no programmatic way to detect (let alone prevent) such screen capture...Zollverein
This also, astoundingly, doesn't prevent anyone from simply taking a photo of the screen with another phone or camera. The whole idea of blocking users from capturing what they're seeing is ridiculously naive.Hag
@SevenSystems is there any system in place that can block a user from taking a photo with another device..?Flaunt
@AlanS that is physically impossible.Hag
@SevenSystems exactly...Flaunt
it looks like WhatsApp have done it - https://mcmap.net/q/139100/-how-to-detect-a-screenshot-on-ios-in-order-to-block-the-content-similar-to-whatsapp-view-once-photos/3361072Paperhanger
S
10

It's been a while, but I just came across ScreenShieldKit, which is a patent-pending technology used by the messaging app Confide. What it does is that it let's the user take screenshots, but the content is blank on the end picture. They recently released the iOS version.

Satterfield answered 12/1, 2018 at 14:31 Comment(4)
It requires adopting their custom UI elements because it is protected by converting the view to video with DRMShelf
@CameronLowellPalmer how do you know they are using DRM?Upstairs
If you dig into their binary you'll see they are doing video transcoding and the whole thing is rather obvious. Apple provides protection for video layers in compliance with copyright law.Shelf
But ScreenShieldKit, supports for only iOS 13 and above. Is there any other way for iOS 12 and below?Sicard
E
5

Remove sensitive information from views before moving to the background. When an application transitions to the background, the system takes a snapshot of the application’s main window, which it then presents briefly when transitioning your application back to the foreground. Before returning from your applicationDidEnterBackground: method, you should hide or obscure passwords and other sensitive personal information that might be captured as part of the snapshot.

In swift 4 add this code to your app delegate.

Declare a variable in app delegate

var imageview : UIImageView?

func applicationWillResignActive(_ application: UIApplication) {

        imageview = UIImageView.init(image: UIImage.init(named: "bg_splash"))
        self.window?.addSubview(imageview!)
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
        // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
    }

func applicationDidBecomeActive(_ application: UIApplication) {
        if (imageview != nil){

            imageview?.removeFromSuperview()
            imageview = nil
        }
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    }
Elizabethelizabethan answered 23/4, 2018 at 21:23 Comment(8)
yeah @AbdulYasin kindly share your optimal solutionElizabethelizabethan
In fact, there is no such way to prevent screenshot in iOS. Though you can use third party library for the same.Hennie
yes there is no way but to hide your information you put a layer of image so that your sensitive information should not go to user and there is no mechanism besides this.Elizabethelizabethan
Not tested yet but looks brilliant. Thanks!Isostasy
#13485016Teddy
This is not relevant to the questionGatepost
This code is not working in check in the xcode11.3 with iOS version 13.3.1Wack
This will only create placeholder view for the background state (app manager).Betake
P
3

You can use this extension below. Please apply this method to the parent view

   extension UIView {
    
    func preventScreenshot(for view: UIView) {
        let textField = UITextField()
        textField.isSecureTextEntry = true
        textField.isUserInteractionEnabled = false
        guard let hiddenView = textField.layer.sublayers?.first?.delegate as? UIView else {
            return
        }
        
        hiddenView.subviews.forEach { $0.removeFromSuperview() }
        hiddenView.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(hiddenView)
        hiddenView.fillSuperview()
        hiddenView.addSubview(view)
    }
}

So for instance to be able to prevent screenshots on a scrollView

private weak var scrollView: UIScrollView! (it's an outlet)

in your viewDidLoad, just do this below

self.view.preventScreenshot(for: self.scrollView)

Note: fillSuperview is just anchoring your view to its superview so it's like below:

NSLayoutConstraint.activate([
        hiddenView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
        hiddenView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
        hiddenView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
        hiddenView.topAnchor.constraint(equalTo: self.topAnchor)
    ])
Psychomotor answered 9/11, 2022 at 10:16 Comment(3)
What's fillSuperview?Reddin
Figured it out. However, this does not work for iOS 12.Reddin
Not working on iOS 17.Yclept
S
2

For iOS 17, Swift 4 or Swift 5, those who are getting issue or crash with the previous implementations. Here is a correct way to do it. I just implemented and tested it on iOS 17+.

Add this code in AppDelegate class:

func makeSecure(window: UIWindow) {
    let field = UITextField()

    let view = UIView(frame: CGRect(x: 0, y: 0, width: field.frame.self.width, height: field.frame.self.height))

    let image = UIImageView(image: UIImage(named: "whiteImage"))
    image.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)

    field.isSecureTextEntry = true

    window.addSubview(field)
    view.addSubview(image)

    window.layer.superlayer?.addSublayer(field.layer)
    field.layer.sublayers?.last!.addSublayer(window.layer)

    field.leftView = view
    field.leftViewMode = .always
}

You can call this from AppDelegate's didFinishLaunchingWithOptions function.

if let window = window {
makeSecure(window: window)
}

This will prevent screen recording as well as ScreenShot in your app.

Swank answered 1/2 at 17:21 Comment(2)
Your method is not working iOS 17.2 simulator.From
@TusharLathiya please test it on a real device. It does not work on SimulatorSwank
P
0

I've heard that you can listen for a screenshot event using UIApplicationUserDidTakeScreenshotNotification

 NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationUserDidTakeScreenshotNotification
                                                  object:nil
                                                   queue:mainQueue
                                              usingBlock:^(NSNotification *note) {
                                                  // executes after screenshot
                                                  NSLog(@"Screenshot Detection : %@", note);
                                                  UIAlertView *screenshotAlert = [[UIAlertView alloc] initWithTitle:@"Screenshot Detected" message:@"Oh Oh no screenshot bruhh" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
                                                  [screenshotAlert show];
                                              }];

what if you could immediately delete the screenshot file when it was made?

Playsuit answered 12/7, 2016 at 6:4 Comment(5)
How would you delete the file? That's not possible AFAIK.Unders
Im not very familiar with ios development, but on android if user gives access to gallery you can create and also delete files from there. So i thought that you could immediately delete the taken screenshot.Playsuit
While this may not help you prevent the screen shot it's a good method to track if anyone is taking screenshots and how prevalent the issue is within your app. If your app uses a login mechanism you can identify the user and restrict app access or provide a notice (if desired) to the user. I implemented this in an app and users were taking screen shots but the amount was small enough to not warrant effort to prevent.Forebode
And what if that user will share the taken screenshot?Hennie
@Unders it is possible, but user permission is requiredHealall
C
0

You can prevent screen capture and screen recording of a specific view in your app by using this custom view. Also, you can add your own placeholder that will only show when the user tries to screen capture or screen record.

import Foundation
import UIKit

class SecureView: UIView {
    
    // placeholder will become visible when user try to capture screenshot
    // or try to record the screen
    private(set) var placeholderView: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    // add your content in this view
    // it will be secure
    private(set) var contentView: UIView = {
        let hiddenView = UIView()
        hiddenView.makeSecure()
        hiddenView.translatesAutoresizingMaskIntoConstraints = false
        return hiddenView
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }
    
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    fileprivate func setupView() {
        
        self.addSubview(placeholderView)
        self.addSubview(contentView)
        
        NSLayoutConstraint.activate([
            placeholderView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
            placeholderView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
            placeholderView.topAnchor.constraint(equalTo: self.topAnchor),
            placeholderView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
            contentView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
            contentView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
            contentView.topAnchor.constraint(equalTo: self.topAnchor),
            contentView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
        ])
    }
    
}

extension UIView {
    func makeSecure() {
        DispatchQueue.main.async {
            let field = UITextField()
            field.isSecureTextEntry = true
            self.addSubview(field)
            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)
        }
    }
}

Sample project

Demo.gif

Crosslegged answered 2/6, 2023 at 14:15 Comment(3)
This does not work for iOS 17.0Johanna
I never tried it with iOS 17.0, I will verify that.Crosslegged
I tried and not working on iOS 17Kimberlite

© 2022 - 2024 — McMap. All rights reserved.