Controlar la captura de pantalla en el conmutador multitarea de iOS 7

98

He estado tratando de encontrar información sobre el nuevo conmutador multitarea en iOS 7 y especialmente la captura de pantalla que toma el sistema operativo cuando la aplicación entra en hibernación.

ingrese la descripción de la imagen aquí

¿Hay alguna forma de desactivar por completo esta función o captura de pantalla? ¿O puedo ocultar la aplicación por completo del conmutador? La aplicación debe ejecutarse en segundo plano, pero no queremos mostrar ninguna captura de pantalla de la aplicación.

La captura de pantalla es potencialmente un riesgo de seguridad, piense en las líneas de aplicaciones bancarias donde su número de tarjeta o resumen de cuenta estará disponible para cualquiera que haga doble clic en el botón de inicio en el dispositivo.

¿Alguien con alguna idea de esto? Gracias.

Tommie
fuente
1
Mi solución actual es cargar la vista en blanco con el logotipo de la aplicación una vez que la aplicación entra en hibernación y eliminarla cuando la aplicación regresa, pero esa solución es difícil de administrar y parece más un truco que una solución elegante. Además, esta solución falla de vez en cuando dependiendo del dispositivo, etc. Por lo tanto, no es realmente útil.
Tommie

Respuestas:

101

Al preparar su interfaz de usuario para ejecutarse en segundo plano , Apple dice:

Prepare su interfaz de usuario para la instantánea de la aplicación

En algún momento después de que su aplicación ingresa en segundo plano y su método delegado regresa, UIKit toma una instantánea de la interfaz de usuario actual de su aplicación. El sistema muestra la imagen resultante en el selector de aplicaciones. También muestra la imagen temporalmente cuando trae su aplicación de nuevo al primer plano.

La interfaz de usuario de su aplicación no debe contener información confidencial del usuario, como contraseñas o números de tarjetas de crédito. Si su interfaz contiene dicha información, elimínela de sus vistas cuando ingrese al fondo. Además, descarte las alertas, las interfaces temporales y los controladores de vista del sistema que ocultan el contenido de su aplicación. La instantánea representa la interfaz de su aplicación y los usuarios deben poder reconocerla. Cuando su aplicación vuelva al primer plano, puede restaurar los datos y las vistas según corresponda.

Consulte las preguntas y respuestas técnicas QA1838: Cómo evitar que aparezca información confidencial en el selector de tareas

Además de ocultar / reemplazar información confidencial, es posible que también desee decirle a iOS 7 que no tome la instantánea de la pantalla a través de ignoreSnapshotOnNextApplicationLaunch, cuya documentación dice:

Si cree que la instantánea no puede reflejar correctamente la interfaz de usuario de su aplicación cuando se relanza su aplicación, puede llamar ignoreSnapshotOnNextApplicationLaunchpara evitar que se tome esa instantánea.

Dicho esto, parece que la instantánea de la pantalla todavía se está tomando y, por lo tanto, he presentado un informe de error. Pero debe probar más y ver si usar esta configuración ayuda.

Si se trataba de una aplicación empresarial, es posible que también desee consultar la configuración adecuada que se allowScreenShotdescribe en la sección Carga útil de restricciones de la Referencia del perfil de configuración.


Aquí hay una implementación que logra lo que necesitaba. Puede presentar el suyo propio UIImageViewo puede utilizar un patrón de protocolo de delegado para ocultar la información confidencial:

//  SecureDelegate.h

#import <Foundation/Foundation.h>

@protocol SecureDelegate <NSObject>

- (void)hide:(id)object;
- (void)show:(id)object;

@end

Luego le di a mi delegado de aplicación una propiedad para eso:

@property (weak, nonatomic) id<SecureDelegate> secureDelegate;

Mi controlador de vista lo configura:

- (void)viewDidLoad
{
    [super viewDidLoad];

    AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
    delegate.secureDelegate = self;
}

El controlador de vista, obviamente, implementa ese protocolo:

- (void)hide:(id)object
{
    self.passwordLabel.alpha = 0.0;
}

- (void)show:(id)object
{
    self.passwordLabel.alpha = 1.0;
}

Y, finalmente, el delegado de mi aplicación se sirve de este protocolo y propiedad:

- (void)applicationWillResignActive:(UIApplication *)application
{
    [application ignoreSnapshotOnNextApplicationLaunch];  // this doesn't appear to work, whether called here or `didFinishLaunchingWithOptions`, but seems prudent to include it

    [self.secureDelegate hide:@"applicationWillResignActive:"];  // you don't need to pass the "object", but it was useful during my testing...
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    [self.secureDelegate show:@"applicationDidBecomeActive:"];
}

Tenga en cuenta que estoy usando en applicationWillResignActivelugar de lo recomendado applicationDidEnterBackground, porque, como han señalado otros, este último no se llama al tocar dos veces el botón de inicio mientras la aplicación se está ejecutando.

Me gustaría poder usar notificaciones para manejar todo esto, en lugar del patrón de protocolo de delegado, pero en mis pruebas limitadas, las notificaciones no se manejan de manera lo suficientemente oportuna, pero el patrón anterior funciona bien.

Robar
fuente
6
Tenga en cuenta que, como se menciona aquí, stackoverflow.com/questions/18937313/… ¡ el comportamiento no es siempre el mismo en el simulador que en el dispositivo!
Michael Forrest
5
Por lo que vale, el ignoreSnapshotOnNextApplicationLaunchmétodo se ignora si no se invoca durante la restauración del estado, como se describe aquí .
Charles A.
3
No es necesario que lo haga en applicationWillResignActive porque la captura de pantalla no se toma hasta applicationDidEnterBackground. Cuando toca dos veces el botón de inicio, no se toma ninguna captura de pantalla; la pantalla que está viendo está en vivo. Agregue una animación a su pantalla para verificar, por ejemplo, ciclos de colores de fondo. Si luego elige otra aplicación, se llamará a su applicationDidEnterBackground antes de tomar la captura de pantalla real.
honor
3
Por cierto, un ingeniero de Apple me informó que la documentación ignoreSnapshotOnNextApplicationLaunches incorrecta, que, como todos observamos, en realidad se toma una instantánea, pero simplemente no se usará en el próximo lanzamiento.
Rob
2
ignoreSnapshotOnNextApplicationLaunch parece hacer lo que parece: evitar que se muestre una captura de pantalla durante el inicio de la aplicación . No afecta la captura de pantalla de la interfaz de usuario multitarea ni si la aplicación se reanuda en lugar de iniciar.
Glenn Maynard
32

Esta es la solución con la que trabajé para mi aplicación:

Como dijo Tommy: puede usar la aplicaciónWillResignActive. Lo que hice fue hacer un UIImageView con mi SplashImage y agregarlo como subvista a mi ventana principal-

(void)applicationWillResignActive:(UIApplication *)application
{
    imageView = [[UIImageView alloc]initWithFrame:[self.window frame]];
    [imageView setImage:[UIImage imageNamed:@"Portrait(768x1024).png"]];
    [self.window addSubview:imageView];
}

Utilicé este método en lugar de applicationDidEnterBackground porque applicationDidEnterBackground no se activará si presiona dos veces el botón de inicio, y applicationWillResignActive lo hará. Escuché a gente decir, aunque también se puede activar en otros casos, así que todavía estoy probando para ver si da problemas, ¡pero ninguno hasta ahora! ;)

Aquí para eliminar la vista de imagen:

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    if(imageView != nil) {
        [imageView removeFromSuperview];
        imageView = nil;
    }
}

¡Espero que esto ayude!

Nota al margen: probé esto tanto en el simulador como en un dispositivo real: no se mostrará en el simulador, ¡pero sí en un dispositivo real!

Laura
fuente
2
Hay un inconveniente: se llama a applicationWillResignActive, entre otros casos, cuando el usuario baja la barra de notificaciones. Así que desliza el dedo de arriba hacia abajo y ve esa imagen, mientras esperaba ver el contenido de la aplicación.
Kirill Gamazkov
4
Es mejor difuminar la información confidencial en applicationWillResignActive y establecer imageView en aplicationDidEnterBackground
Kirill Gamazkov
Esto funciona, pero hay un error de rotación cuando se implementa en iOS7, construido con xCode6: stackoverflow.com/questions/26147068/…
Alex Stone
Si escribo el código que mencionaste en applicationWillResignActive , ¿qué veré cuando devuelva mi aplicación al estado de primer plano? ¿Veré la imagen "Retrato (768x1024) .png" o la pantalla real?
NSPratik
2
@Pratik: Verá la imagen, a menos que la elimine nuevamente en el método applicationDidBecomeActive (ver arriba).
Laura
14

Este método rápido y fácil producirá una instantánea negra sobre el icono de su aplicación en el conmutador de aplicaciones iOS7 o posterior.

Primero, tome la ventana clave de su aplicación (generalmente configurada en AppDelegate.m en la aplicación: didFinishLaunchingWithOptions) y ocúltela cuando su aplicación esté a punto de pasar al fondo:

- (void)applicationWillResignActive:(UIApplication *)application
{
    if(isIOS7Or8)
    {
        self.window.hidden = YES;
    }
}

Luego, muestre la ventana clave de su aplicación cuando su aplicación se active nuevamente:

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    if(isIOS7Or8)
    {
        self.window.hidden = NO;
    }
}

En este punto, revise el selector de aplicaciones y verifique que vea una instantánea negra sobre el ícono de su aplicación. Me he dado cuenta de que si inicia el conmutador de aplicaciones inmediatamente después de mover su aplicación en segundo plano, puede haber una demora de ~ 5 segundos en la que verá una instantánea de su aplicación (¡la que desea ocultar!), Después que cambia a una instantánea completamente negra. No estoy seguro de qué pasa con el retraso; Si alguien tiene alguna sugerencia, por favor intervenga.

Si desea un color diferente al negro en el conmutador, puede hacer algo como esto agregando una subvista con cualquier color de fondo que desee:

- (void)applicationWillResignActive:(UIApplication *)application
{
    if(isIOS7Or8)
    {
        UIView *colorView = [[[UIView alloc] initWithFrame:self.window.frame] autorelease];
        colorView.tag = 9999;
        colorView.backgroundColor = [UIColor purpleColor];
        [self.window addSubview:colorView];
        [self.window bringSubviewToFront:colorView];
    }
}

Luego, elimine esta subvista de color cuando su aplicación se active nuevamente:

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    if(isIOS7Or8)
    {
        UIView *colorView = [self.window viewWithTag:9999];
        [colorView removeFromSuperview];
    }
}
John Jacecko
fuente
4

Usé la siguiente solución: cuando la aplicación va a renunciar, obtengo una instantánea de appWindow como Vista y le agrego desenfoque. Luego agrego esta vista a la ventana de la aplicación

como hacer esto:

  1. en appDelegate justo antes de la implementación, agregue la línea:

    static const int kNVSBlurViewTag = 198490;//or wherever number you like
  2. agregue estos métodos:

    - (void)nvs_blurPresentedView
        {
            if ([self.window viewWithTag:kNVSBlurViewTag]){
                return;
            }
            [self.window addSubview:[self p_blurView]];
        }
    
        - (void)nvs_unblurPresentedView
        {
            [[self.window viewWithTag:kNVSBlurViewTag] removeFromSuperview];
        }
    
        #pragma mark - Private
    
        - (UIView *)p_blurView
        {
            UIView *snapshot = [self.window snapshotViewAfterScreenUpdates:NO];
    
            UIView *blurView = nil;
            if ([UIVisualEffectView class]){
                UIVisualEffectView *aView = [[UIVisualEffectView alloc]initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]];
                blurView        = aView;
                blurView.frame  = snapshot.bounds;
                [snapshot addSubview:aView];
            }
            else {
                UIToolbar *toolBar  = [[UIToolbar alloc] initWithFrame:snapshot.bounds];
                toolBar.barStyle    = UIBarStyleBlackTranslucent;
                [snapshot addSubview:toolBar];
            }
            snapshot.tag = kNVSBlurViewTag;
            return snapshot;
        }
  3. haga que su implementación de appDelegate sea la siguiente:

    - (void)applicationWillResignActive:(UIApplication *)application {
        // 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 throttle down OpenGL ES frame rates. Games should use this method to pause the game.
        //...
        //your code
        //...
    
        [self nvs_blurPresentedView];
    }
    
        - (void)applicationWillEnterForeground:(UIApplication *)application {
        // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
        //...
        //your code
        //...
    
        [self nvs_unblurPresentedView];
    }

Creé ejemplos de proyectos en Swift y Objective C . Ambos proyectos realizan las siguientes acciones en:

-application: didResignActive : la instantánea se crea, se difumina y se agrega a la ventana de la aplicación

-aplicación: la vista borrosa willBecomeActive se está eliminando de la ventana.

Cómo utilizar:

Objeto C

  1. Agregue AppDelegate+NVSBlurAppScreenarchivos .hy .m a su proyecto

  2. en su método -applicationWillResignActive: agregue la siguiente línea:

    [self nvs_blurPresentedView];
  3. en su método -applicationDidEnterBackground: agregue la siguiente línea:

    [self nvs_unblurPresentedView];

Rápido

  1. agregue el archivo AppDelegateExtention.swift a su proyecto

  2. en su función applicationWillResignActive agregue la siguiente línea:

    blurPresentedView()
  3. en su función applicationDidBecomeActive agregue la siguiente línea:

    unblurPresentedView()
Nikolay Shubenkov
fuente
2

si solo se usa [self.window addSubview:imageView];en la applicationWillResignActivefunción, este imageView no cubrirá UIAlertView, UIActionSheet o MFMailComposeViewController ...

La mejor solución es

- (void)applicationWillResignActive:(UIApplication *)application
{
    UIWindow *mainWindow = [[[UIApplication sharedApplication] windows] lastObject];
    [mainWindow addSubview:imageView];
}
Zorot
fuente
¡Muy buena solución si tiene un video que se reproduce en modo de pantalla completa en una vista web!
Tommie
¡Excelente!. ¡Funciona incluso si tenemos un teclado abierto antes de entrar en el conmutador multitarea! Gracias
Prabhu.Somasundaram
0

Proporcionar mi propia solución como "respuestas", aunque esta solución es muy poco confiable. A veces obtengo una pantalla negra como captura de pantalla, a veces el XIB y, a veces, una captura de pantalla de la propia aplicación. Dependiendo del dispositivo y / o si ejecuto esto en el simulador.

Tenga en cuenta que no puedo proporcionar ningún código para esta solución ya que contiene muchos detalles específicos de la aplicación. Pero esto debería explicar la esencia básica de mi solución.

En AppDelegate.m bajo applicationWillResignActive verifico si estamos ejecutando iOS7, si lo hacemos, cargo una nueva vista que está vacía con el logotipo de la aplicación en el medio. Una vez que se llama applicationDidBecomeActive, vuelvo a ejecutar mis vistas anteriores, que se restablecerán, pero eso funciona para el tipo de aplicación que estoy desarrollando.

Tommie
fuente
1
Si ve lo que hace la aplicación de la cámara, cambia la imagen a una borrosa cuando pasa al fondo (puede ver la transición a medida que se reduce al icono)
Petesh
@Petesh Sí, es una buena implementación, pero la pregunta es cómo lo hacen. Temo una API privada.
Rob
@Tommie Dices "Dependiendo del dispositivo y / o si ejecuto esto en el simulador". Para mí, parece funcionar en el dispositivo (aunque no he realizado pruebas exhaustivas), pero no en el simulador. ¿Está encontrando que no funciona en el dispositivo?
Rob
0

Puede usar el activador para configurar el doble clic del botón de inicio para iniciar la multitarea y deshabilitar el doble clic predeterminado del botón de inicio y el inicio de la ventana de multitarea. Este método se puede utilizar para cambiar las capturas de pantalla a la imagen predeterminada de la aplicación. Esto es aplicable a aplicaciones con la función de protección de contraseña predeterminada.

Fareed.MK
fuente
-2

Xamarin.iOS

Adaptado de https://stackoverflow.com/a/20040270/7561

En lugar de solo mostrar un color, quería mostrar mi pantalla de inicio.

 public override void DidEnterBackground(UIApplication application)
 {
    //to add the background image in place of 'active' image
    var backgroundImage = new UIImageView();
    backgroundImage.Tag = 1234;
    backgroundImage.Image = UIImage.FromBundle("Background");
    backgroundImage.Frame = this.window.Frame;
    this.window.AddSubview(backgroundImage);
    this.window.BringSubviewToFront(backgroundImage);
}

public override void WillEnterForeground(UIApplication application)
{
    //remove 'background' image
    var backgroundView = this.window.ViewWithTag(1234);
    if(null != backgroundView)
        backgroundView.RemoveFromSuperview();
}
ben
fuente