iOS Detection of Screenshot?
Asked Answered
F

7

157

The app Snapchat, on the App Store, is an app that lets you share pictures with a self-destruct on them. You can only view the pics for X seconds. If you attempt to take a screenshot while the picture is showing using the home-power key combo, it will tell the sender you tried to take a screenshot.

What part of the SDK lets you detect that the user is taking a screenshot? I did not know this was possible.

Forwarder answered 21/11, 2012 at 0:27 Comment(5)
#2122470, Looks like it used to call -applicationDidEnterBackground: before taking the screenshot earlier. Not sure about that now.Papacy
Guys. The other thread has the answer: #2122470Forwarder
check this as well, https://mcmap.net/q/139162/-how-to-detect-if-user-did-a-screenshot-with-home-power-in-ios, it says it is not possible any more. Probably you can try that out and let us know.Papacy
Haven't seen this mentioned anywhere on the Internet yet, but I would assume that if you use Xcode to take a screenshot (from the device in the Organizer window), there's absolutely no way the app will be able to know. It must be monitoring the Camera Roll for any photos added while viewing a received Snapchat photo, and taking the screenshot via Xcode bypasses this altogether (without the need for jailbreaking).Fed
Follow-up: Tested this theory and confirmed that the app does not detect Xcode screenshots. However, I realized what's interesting is that on iOS 6, apps must explicitly be granted permission to access photos...yet this app still detects screenshots without allowing it to access photos! It must be using another method for detection -- I'm noticing that when using the Home+Sleep button method, the active photo is removed from the screen as well. So there must be some pattern related to this screenshot process the app can reliably monitor, perhaps with a GestureRecognizer?Fed
T
26

I found the answer!! Taking a screenshot interrupts any touches that are on the screen. This is why snapchat requires holding to see the picture. Reference: http://tumblr.jeremyjohnstone.com/post/38503925370/how-to-detect-screenshots-on-ios-like-snapchat

Towhead answered 2/1, 2013 at 10:37 Comment(3)
No longer true with iOS 7. See below for an iOS7+ solution.Zygote
What Joe said is correct. Asker should uncheck this as the proper answer.Cassella
UIApplication​User​Did​Take​Screenshot​Notification can be used .. iOS 7+Groceryman
G
371

As of iOS 7 the other answers are no longer true. Apple has made it so touchesCancelled:withEvent: is no longer called when the user takes a screenshot.

This would effectively break Snapchat entirely, so a couple betas in a new solution was added. Now, the solution is as simple as using NSNotificationCenter to add an observer to UIApplicationUserDidTakeScreenshotNotification.

Here's an example:

Objective C

NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationUserDidTakeScreenshotNotification
                                                  object:nil
                                                   queue:mainQueue
                                              usingBlock:^(NSNotification *note) {
                                                 // executes after screenshot
                                              }];

Swift

NotificationCenter.default.addObserver(
    forName: UIApplication.userDidTakeScreenshotNotification,
    object: nil,
    queue: .main) { notification in
        //executes after screenshot
}
Given answered 10/8, 2013 at 3:55 Comment(13)
Using this and touchesCancelled:withEvent: should allow you to detect a screenshot on all (recent) versions of iOS.Almeria
This does not work to prevent a screenshot from being taken. It can only let the app know that one was taken.. From the UIApplication Class Reference: UIApplicationUserDidTakeScreenshotNotification Posted when the user presses the Home and Lock buttons to take a screenshot. This notification does not contain a userInfo dictionary. This notification is posted AFTER the screenshot is taken.Perionychium
@Perionychium Correct. Considering this notification follows conventional Cocoa naming conventions, the word "Did" implies that it is posted after the fact. There is no "Will" equivalent in this case, and AFAIK no way to prevent the user from taking a screenshot using public API.Given
Note that I gave you a +1. I had misread the OP question originally and thought the question was how to detect it to prevent something - because that's what I was looking for. So I just added the clarification in the comment because I expect a lot of people coming to this question are looking for that answer. I assumed that as well from the word "did" but the documentation makes it even more clear. In my app I'm allowing people to edit photos but some of the tools require IAPs. But I let them try before buy. So I wanted to detect before it was captured to add a watermark. Can't be done.Perionychium
There's no way to prevent that screenshot from making it into the user's photos and therefore out of the purview of the app.Cassella
sometimes we need to execute something while user is taking screenshot not didTakeScreenshot !! is there any solution for that ?Ulla
@Mc.Lover No, there is not.Given
@MickMacCallum Any specific reason you're doing it on main queue?Khanna
I've tried your solution but failed. But I do get the notification by using ` NotificationCenter.default.addObserver(self, selector: #selector(myHandlerFunction), name: NSNotification.Name.UIApplicationUserDidTakeScreenshot, object: nil) `. Do you have any idea why this happens? I'll appreciate your help. Thanks. @MickMacCallumThrippence
@Thrippence it's probably the observer returned by addObserver being deallocated before the notification fires. You can either use the solution you've come up with, or assign the return of addObserver(forName... to an instance variable of type NSObjectProtocol. If you go this route, you'll need to make sure you call NotificationCenter.default.removeObserver(someObserverIvar) as well.Given
@MickMacCallum Happy to see your reply. I’ll try both and thanks again. :)Thrippence
Is there any way to detect captured image in the block and we can make it obscured?Cryometer
does this work on simulator or we need a real device. ?Paphos
T
26

I found the answer!! Taking a screenshot interrupts any touches that are on the screen. This is why snapchat requires holding to see the picture. Reference: http://tumblr.jeremyjohnstone.com/post/38503925370/how-to-detect-screenshots-on-ios-like-snapchat

Towhead answered 2/1, 2013 at 10:37 Comment(3)
No longer true with iOS 7. See below for an iOS7+ solution.Zygote
What Joe said is correct. Asker should uncheck this as the proper answer.Cassella
UIApplication​User​Did​Take​Screenshot​Notification can be used .. iOS 7+Groceryman
D
16

Heres how to do in Swift with closures:

func detectScreenShot(action: () -> ()) {
    let mainQueue = NSOperationQueue.mainQueue()
    NSNotificationCenter.defaultCenter().addObserverForName(UIApplicationUserDidTakeScreenshotNotification, object: nil, queue: mainQueue) { notification in
        // executes after screenshot
        action()
    }
}

detectScreenShot { () -> () in
    print("User took a screen shot")
}

Swift 4.2

func detectScreenShot(action: @escaping () -> ()) {
    let mainQueue = OperationQueue.main
    NotificationCenter.default.addObserver(forName: UIApplication.userDidTakeScreenshotNotification, object: nil, queue: mainQueue) { notification in
        // executes after screenshot
        action()
    }
}

This is included as a standard function in:

https://github.com/goktugyil/EZSwiftExtensions

Disclaimer: Its my repo

Deboer answered 27/11, 2015 at 2:43 Comment(8)
Hey, I tried this and it worked great, but can you explain what's going on in the code a little? I'm new to Swift and it's a bit hard to read.Stoush
This is one of those, "if it works don't mess with it" kind of codes. You don't need to learn what this does cause the frameworks used in here are very rare.Deboer
But you should checkout how closures work if you don't know that part, its basically when you call detect screen shot func, what ever you put in the parantheses is sent as an action functionDeboer
@Deboer Any specific reason you're doing it on the main queue?Khanna
Cause of copy pasteDeboer
Removing observer in didDisappear doest not work. Solution? I get multiple notificationsMut
The question is asking how to be notified before the screenshot is taken. This tells you after is has been taken.Putrescible
Is there any way to detect captured image in the block and we can make it obscured?Cryometer
S
8

Swift 4+

NotificationCenter.default.addObserver(forName: UIApplication.userDidTakeScreenshotNotification, object: nil, queue: OperationQueue.main) { notification in
           //you can do anything you want here. 
        }

by using this observer you can find out when user takes a screenshot, but you can not prevent him.

Strapper answered 22/9, 2019 at 8:42 Comment(0)
M
2

Latest SWIFT 3:

func detectScreenShot(action: @escaping () -> ()) {
        let mainQueue = OperationQueue.main
        NotificationCenter.default.addObserver(forName: .UIApplicationUserDidTakeScreenshot, object: nil, queue: mainQueue) { notification in
            // executes after screenshot
            action()
        }
    }

In viewDidLoad, call this function

detectScreenShot { () -> () in
 print("User took a screen shot")
}

However,

NotificationCenter.default.addObserver(self, selector: #selector(test), name: .UIApplicationUserDidTakeScreenshot, object: nil)

    func test() {
    //do stuff here
    }

works totally fine. I don't see any points of mainQueue...

Mut answered 2/7, 2017 at 7:3 Comment(3)
The question is asking how to be notified before the screenshot is taken. This tells you after is has been taken.Putrescible
@Putrescible where did you saw that question is asking how to be notified before? I only improved the answer above me, not sure your comment intention ..Mut
The question asks: "detect that the user is taking a screenshot". If the OP wanted to know after the fact, the question should read: "detect that the user took a screenshot".Putrescible
P
1

Looks like there are no direct way to do this to detect if user has tapped on home + power button. As per this, it was possible earlier by using darwin notification, but it doesn't work any more. Since snapchat is already doing it, my guess is that they are checking the iPhone photo album to detect if there is a new picture got added in between this 10 seconds, and in someway they are comparing with the current image displayed. May be some image processing is done for this comparison. Just a thought, probably you can try to expand this to make it work. Check this for more details.

Edit:

Looks like they might be detecting the UITouch cancel event(Screen capture cancels touches) and showing this error message to the user as per this blog: How to detect screenshots on iOS (like SnapChat)

In that case you can use – touchesCancelled:withEvent: method to sense the UITouch cancellation to detect this. You can remove the image in this delegate method and show an appropriate alert to the user.

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesCancelled:touches withEvent:event];

    NSLog(@"Touches cancelled");

    [self.imageView removeFromSuperView]; //and show an alert to the user
}
Papacy answered 21/11, 2012 at 0:49 Comment(5)
you seem to be well connected in the right places to get a definitive answer about this ;)Fed
It is more of an educated guess than a definitive answer. Unfortunately I don't have any connections to get an exact answer for this. If they are not using any private APIs, that is the only way I can think of, for doing this. To detect the image addition to album and compare that image with current image in screen based on some algorithm.Papacy
But given that they can do this without requesting access to the device's photos and Camera Roll...it must be something else no? My theory would be related to the fact they make you long press on the received photo message to view it, and that when you press Home + Lock buttons, the OS immediately acts as though no fingers are touching the screen. Maybe this happens without a touchesEnded:withEvent (or similar callback) as it normally would, so perhaps they can monitor for this unique pattern of events? I may be totally on the wrong track, but that's my only theory at this point.Fed
Put a finger on the screen and without lifting it, try if you can press the other two buttons. It was still showing that message I guess. So may be they are using some private API and somehow managed to put in appstore.Papacy
No longer possible as of iOS 7.Cassella
S
0

Swift 4 Examples

Example #1 using closure

NotificationCenter.default.addObserver(forName: .UIApplicationUserDidTakeScreenshot, 
                                       object: nil, 
                                       queue: OperationQueue.main) { notification in
    print("\(notification) that a screenshot was taken!")
}

Example #2 with selector

NotificationCenter.default.addObserver(self, 
                                       selector: #selector(screenshotTaken), 
                                       name: .UIApplicationUserDidTakeScreenshot, 
                                       object: nil)

@objc func screenshotTaken() {
    print("Screenshot taken!")
}
Stainless answered 9/8, 2019 at 16:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.