Detección de iOS de captura de pantalla?

135

La aplicación Snapchat , en la App Store, es una aplicación que te permite compartir imágenes con una autodestrucción en ellas. Solo puedes ver las fotos durante X segundos. Si intentas tomar una captura de pantalla mientras se muestra la imagen usando el combo de la tecla de encendido, le indicará al remitente que intentaste tomar una captura de pantalla.

¿Qué parte del SDK le permite detectar que el usuario está tomando una captura de pantalla? No sabía que esto era posible.

me2
fuente
1
stackoverflow.com/questions/2121970/… , parece que solía llamar a -applicationDidEnterBackground: antes de tomar la captura de pantalla antes. No estoy seguro de eso ahora.
iDev
Chicos El otro hilo tiene la respuesta: stackoverflow.com/questions/2121970/…
me2
1
compruebe esto también, stackoverflow.com/a/8711894/1730272 , dice que ya no es posible. Probablemente puedas probar eso y hacérnoslo saber.
iDev
Todavía no he visto esto mencionado en ninguna parte de Internet, pero supongo que si usa Xcode para tomar una captura de pantalla (desde el dispositivo en la ventana del Organizador), no hay absolutamente ninguna manera de que la aplicación pueda saberlo. Debe estar monitoreando el Rollo de la cámara para ver las fotos agregadas mientras se ve una foto de Snapchat recibida, y tomar la captura de pantalla a través de Xcode lo omite por completo (sin la necesidad de hacer jailbreak).
smileyborg
Seguimiento: probó esta teoría y confirmó que la aplicación no detecta capturas de pantalla de Xcode. Sin embargo, me di cuenta de que lo interesante es que en iOS 6, las aplicaciones deben tener permiso explícito para acceder a las fotos ... ¡pero esta aplicación aún detecta capturas de pantalla sin permitirles acceder a las fotos! Debe estar usando otro método para la detección: me doy cuenta de que cuando se usa el método del botón Inicio + Dormir, la foto activa también se elimina de la pantalla. Entonces, ¿debe haber algún patrón relacionado con este proceso de captura de pantalla que la aplicación pueda monitorear de manera confiable, tal vez con un GestureRecognizer?
smileyborg

Respuestas:

22

¡Encontré la respuesta! Tomar una captura de pantalla interrumpe cualquier toque que esté en la pantalla. Esta es la razón por la cual Snapchat requiere esperar para ver la imagen. Referencia: http://tumblr.jeremyjohnstone.com/post/38503925370/how-to-detect-screenshots-on-ios-like-snapchat

portforwardpodcast
fuente
15
Ya no es cierto con iOS 7. Vea a continuación una solución iOS7 +.
Joe Masilotti
66
Lo que Joe dijo es correcto. Asker debería desmarcar esto como la respuesta adecuada.
Dios de las galletas
El usuario de la aplicación UIA tomó la captura de pantalla Se puede usar la notificación. IOS 7+
Amit Tandel
353

A partir de iOS 7, las otras respuestas ya no son ciertas. Apple ha logrado que touchesCancelled:withEvent:ya no se llame cuando el usuario toma una captura de pantalla.

Esto efectivamente rompería Snapchat por completo, por lo que se agregaron un par de versiones beta en una nueva solución. Ahora, la solución es tan simple como usar NSNotificationCenter para agregar un observador a UIApplicationUserDidTakeScreenshotNotification .

Aquí hay un ejemplo:

C objetivo

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

Rápido

NotificationCenter.default.addObserver(
    forName: UIApplication.userDidTakeScreenshotNotification,
    object: nil,
    queue: .main) { notification in
        //executes after screenshot
}
Mick MacCallum
fuente
3
Usar esto y touchesCancelled:withEvent:debería permitirle detectar una captura de pantalla en todas las versiones (recientes) de iOS.
Joshua Gross
46
Esto no funciona para evitar que se tome una captura de pantalla. Solo puede hacer que la aplicación sepa que se tomó una. De la Referencia de clase de aplicación UIA: UIApplicationUserDidTakeScreenshotNotification Publicado cuando el usuario presiona los botones Inicio y Bloqueo para tomar una captura de pantalla. Esta notificación no contiene un diccionario userInfo. Esta notificación se publica DESPUÉS de que se toma la captura de pantalla.
badweasel
66
@badweasel Correcto. Teniendo en cuenta que esta notificación sigue las convenciones convencionales de denominación de cacao, la palabra "Did" implica que se publica después del hecho. No hay un equivalente de "Voluntad" en este caso, y AFAIK no puede evitar que el usuario tome una captura de pantalla usando una API pública.
Mick MacCallum
1
Tenga en cuenta que le di un +1. Había leído mal la pregunta OP originalmente y pensé que la pregunta era cómo detectarla para evitar algo, porque eso era lo que estaba buscando. Así que acabo de agregar la aclaración en el comentario porque espero que muchas personas que vienen a esta pregunta estén buscando esa respuesta. Asumí eso también por la palabra "did", pero la documentación lo deja aún más claro. En mi aplicación, estoy permitiendo que la gente edite fotos, pero algunas de las herramientas requieren IAP. Pero los dejé probar antes de comprar. Así que quería detectar antes de que fuera capturado para agregar una marca de agua. No se puede hacer
badweasel
1
@MickMacCallum ¿Alguna razón específica por la que lo estás haciendo en la cola principal?
kidsid49
13

Aquí está cómo hacer en Swift con cierres:

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

Esto se incluye como una función estándar en:

https://github.com/goktugyil/EZSwiftExtensions

Descargo de responsabilidad: es mi repositorio

Esqarrouth
fuente
Oye, probé esto y funcionó muy bien, pero ¿puedes explicar un poco lo que sucede en el código? Soy nuevo en Swift y es un poco difícil de leer.
Ascender
Este es uno de esos códigos de tipo "si funciona, no te metas con él". No necesita saber qué hace esto porque los marcos utilizados aquí son muy raros.
Esqarrouth
Pero debe verificar cómo funcionan los cierres si no conoce esa parte, es básicamente cuando llama a la función de detección de captura de pantalla, lo que ponga en las paréntesis se envía como una función de acción
Esqarrouth
@Esqarrouth ¿Alguna razón específica por la que lo estás haciendo en la cola principal?
kidsid49
Causa de copiar y pegar
Esqarrouth
4

Última 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()
        }
    }

En viewDidLoad , llame a esta función

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

Sin embargo,

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

    func test() {
    //do stuff here
    }

Funciona totalmente bien. No veo ningún punto de mainQueue ...

Maksim Kniazev
fuente
La pregunta es cómo recibir una notificación antes de tomar la captura de pantalla. Esto le indica después de que se haya tomado.
rmaddy
1
@rmaddy, ¿dónde viste que esa pregunta es cómo ser notificado antes? Solo mejoré la respuesta por encima de mí, no estoy seguro de la intención de tu comentario ...
Maksim Kniazev
La pregunta es: "detectar que el usuario está tomando una captura de pantalla" . Si el OP quería saber después del hecho, la pregunta debería leer: "detectar que el usuario tomó una captura de pantalla" .
rmaddy
1

Parece que no hay una forma directa de hacer esto para detectar si el usuario ha hecho tapping home + power button. Según esto , fue posible antes mediante el uso de la notificación de Darwin, pero ya no funciona. Dado que Snapchat ya lo está haciendo, supongo que están revisando el álbum de fotos del iPhone para detectar si hay una nueva imagen agregada entre estos 10 segundos, y de alguna manera se están comparando con la imagen actual que se muestra. Puede haber algún procesamiento de imagen para esta comparación. Solo un pensamiento, probablemente pueda intentar expandir esto para que funcione. Mira esto para más detalles .

Editar:

Parece que podrían estar detectando el evento de cancelación de UITouch (la captura de pantalla cancela los toques) y mostrando este mensaje de error al usuario según este blog: Cómo detectar capturas de pantalla en iOS (como SnapChat)

En ese caso, puede utilizar el – touchesCancelled:withEvent:método para detectar la cancelación de UITouch para detectar esto. Puede eliminar la imagen en este método de delegado y mostrar una alerta apropiada al usuario.

- (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
}
iDev
fuente
parece estar bien conectado en los lugares correctos para obtener una respuesta definitiva sobre esto;)
smileyborg
Es más una suposición educada que una respuesta definitiva. Lamentablemente, no tengo ninguna conexión para obtener una respuesta exacta para esto. Si no están utilizando ninguna API privada, esa es la única forma en que puedo pensar, para hacer esto. Para detectar la adición de imagen al álbum y comparar esa imagen con la imagen actual en la pantalla basada en algún algoritmo.
iDev
Pero dado que pueden hacer esto sin solicitar acceso a las fotos del dispositivo y Camera Roll ... ¿debe ser otra cosa, no? Mi teoría se relacionaría con el hecho de que hacen que mantenga presionado el mensaje de foto recibido para verlo, y que cuando presiona los Home + Lockbotones, el sistema operativo actúa inmediatamente como si ningún dedo estuviera tocando la pantalla. Tal vez esto suceda sin una touchesEnded:withEvent(o una devolución de llamada similar) como lo haría normalmente, ¿entonces tal vez puedan monitorear este patrón único de eventos? Puedo estar totalmente en el camino equivocado, pero esa es mi única teoría en este momento.
smileyborg
Coloque un dedo en la pantalla y, sin levantarlo, pruebe si puede presionar los otros dos botones. Todavía estaba mostrando ese mensaje, supongo. Por lo tanto, es posible que estén utilizando alguna API privada y de alguna manera lograron poner en la tienda de aplicaciones.
iDev
2
Ya no es posible a partir de iOS 7.
God of Biscuits
1

Swift 4+

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

Al usar este observador puede averiguar cuándo el usuario toma una captura de pantalla, pero no puede evitarlo.

Hamid Shahsavari
fuente
0

Swift 4 Ejemplos

Ejemplo # 1 usando cierre

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

Ejemplo # 2 con selector

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

@objc func screenshotTaken() {
    print("Screenshot taken!")
}
Devbot10
fuente