76

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?

Community
  • 1
  • 1
zolio
  • 2,439
  • 4
  • 22
  • 34
  • 3
    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. – mash Sep 08 '13 at 03:15
  • 3
    I don't think you can prevent it on a non-jailbroken device, but you can detect it - http://stackoverflow.com/questions/13484516/ios-detection-of-screenshot – tacos_tacos_tacos Sep 08 '13 at 04:05
  • Is there any way to detect captured image in the block and we can make it obscured? – Sunil Targe Feb 22 '19 at 03:04
  • I already saw some security frameworks who do that, but I don't know exactly what they do, but certainly some system call interception. – Carla Camargo Jan 20 '20 at 12:18
  • 1
    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 screen – JimBobBennett Oct 12 '14 at 10:37
  • Do you solve the issue? – Serhii Didanov Nov 20 '20 at 11:20

7 Answers7

69

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
            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.

orakull
  • 811
  • 6
  • 6
  • It's a bit like magic, but the approach actually works! It messes with the autolayout though... any idea how to fix it? – yspreen Apr 21 '21 at 20:48
  • Also, what's your source on this? It's seemingly mentioned nowhere on the internet – yspreen Apr 21 '21 at 21:14
  • This is my own elaboration :) I also tried to find something similar on the internet, but couldn't – orakull Apr 27 '21 at 14:16
  • 6
    It should also be noted that this will only work on iOS 13+ – orakull Apr 27 '21 at 14:19
  • 15
    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. – mradzinski Apr 28 '21 at 17:23
  • I implement this on my react-native project, works great, thanks a lot – Nald Dev Jun 12 '21 at 06:24
  • 5
    @orakull `_UITextLayoutCanvasView` is responsible for hiding things (see https://gist.github.com/damian-rzeszot/bbfd92895dfe9400ba9a7cf2a0c1e670) – Damian Rzeszot Nov 16 '21 at 01:13
  • @NaldDev sorry for necroposting but could you share your RN implementation? For some reason it does not work for me :( – qwerty qwerty Nov 26 '21 at 15:51
  • As @mradzinski 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. – curiousSloth Feb 10 '22 at 21:00
  • Works like a charm in a normal controller. Hopefully I will be able to integrate it into a PDF reader. – Subash Parajuli Feb 15 '22 at 08:12
  • Minimizing and opening back the app the view will be red in color. How can we toggle it back to its original state? – joy son Feb 25 '22 at 10:17
  • @orakull why is it working on iOS 13 and above. All the functionalities and members used are iOS 9 and above. Any specific reason. – Ankit Kumar Gupta Apr 27 '22 at 06:46
  • @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) – orakull Apr 28 '22 at 17:28
  • hi, can i write this code in objective-c? – Just L Nov 04 '22 at 09:11
  • Can anybody explain how it works? – Harsha Dec 20 '22 at 06:17
  • This hack still works! – Waaheeda Jul 07 '23 at 17:20
  • 3
    I'm not longer seeing this approach working on iOS 17. Has anyone identified an alternative approach? – notclive Jul 31 '23 at 15:27
  • What about SwiftUI? How do I apply this in a SwiftUI app? – acmpo6ou Aug 05 '23 at 09:48
31

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?

Community
  • 1
  • 1
Joshua Gross
  • 526
  • 6
  • 10
  • 2
    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... – Nicolas Miari May 23 '16 at 01:46
  • 1
    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. – Seven Systems Oct 01 '22 at 18:50
  • @SevenSystems is there any system in place that can block a user from taking a photo with another device..? – Alan S Feb 20 '23 at 16:02
  • @AlanS that is physically impossible. – Seven Systems Feb 22 '23 at 22:12
  • @SevenSystems exactly... – Alan S Feb 23 '23 at 10:45
  • it looks like WhatsApp have done it - https://stackoverflow.com/q/76389835/3361072 – Alexandru Motoc Jun 02 '23 at 11:44
9

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.

Onur Çevik
  • 1,560
  • 13
  • 21
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.
    }
OurangZeb Khan
  • 1,114
  • 18
  • 20
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)
    ])
alidinc
  • 51
  • 6
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?

lxknvlk
  • 2,744
  • 1
  • 27
  • 32
  • 2
    How would you delete the file? That's not possible AFAIK. – BadmintonCat Sep 08 '16 at 03:40
  • 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. – lxknvlk Sep 08 '16 at 07:11
  • 2
    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. – xdeleon May 27 '18 at 17:04
  • And what if that user will share the taken screenshot? – Abdul Yasin Aug 20 '18 at 08:04
  • @BadmintonCat it is possible, but user permission is required – Mykyta Savchuk Sep 30 '19 at 06:47
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

Sreekuttan
  • 1,579
  • 13
  • 19