Controllo dello screenshot nello switcher multitasking di iOS 7


98

Ho cercato di trovare alcune informazioni sul nuovo commutatore multitasking in iOS 7 e in particolare lo screenshot che il sistema operativo acquisisce quando l'app è in ibernazione.

inserisci qui la descrizione dell'immagine

C'è un modo per disattivare completamente questa funzione o screenshot? O posso nascondere completamente l'app dallo switcher? L'app deve essere eseguita in background, ma non vogliamo mostrare alcuno screenshot dall'app.

Lo screenshot è potenzialmente un rischio per la sicurezza, pensa sulla falsariga delle app bancarie in cui il numero della tua carta o il riepilogo del conto saranno disponibili a chiunque faccia doppio clic sul pulsante Home sul dispositivo.

Qualcuno con qualche intuizione in questo? Grazie.


1
La mia soluzione attuale è caricare la vista vuota con il logo dell'app una volta che l'app entra in ibernazione e rimuoverla quando l'app ritorna, ma quella soluzione è difficile da gestire e sembra un hack piuttosto che una soluzione elegante. Inoltre, questa soluzione di tanto in tanto non riesce a seconda del dispositivo, ecc. Quindi non è realmente utilizzabile.
Tommie

Risposte:


101

Nel preparare la tua interfaccia utente per l'esecuzione in background , Apple afferma:

Prepara la tua interfaccia utente per l'istantanea dell'app

Ad un certo punto dopo che l'app è entrata in background e il metodo delegato è tornato, UIKit acquisisce un'istantanea dell'interfaccia utente corrente dell'app. Il sistema visualizza l'immagine risultante nel commutatore di app. Inoltre, visualizza temporaneamente l'immagine quando si riporta la tua app in primo piano.

L'interfaccia utente della tua app non deve contenere informazioni sensibili sugli utenti, come password o numeri di carte di credito. Se la tua interfaccia contiene tali informazioni, rimuovile dalle tue visualizzazioni quando inserisci lo sfondo. Inoltre, ignora gli avvisi, le interfacce temporanee e i controller di visualizzazione del sistema che oscurano il contenuto della tua app. L'istantanea rappresenta l'interfaccia della tua app e dovrebbe essere riconoscibile dagli utenti. Quando la tua app torna in primo piano, puoi ripristinare dati e visualizzazioni in base alle esigenze.

Vedere Domande e risposte tecniche QA1838: Come impedire la visualizzazione di informazioni sensibili nel selettore di attività

Oltre a nascondere / sostituire le informazioni sensibili, potresti anche voler dire a iOS 7 di non acquisire l'istantanea dello schermo tramite ignoreSnapshotOnNextApplicationLaunch, la cui documentazione dice:

Se ritieni che l'istantanea non possa riflettere correttamente l'interfaccia utente della tua app quando l'app viene riavviata, puoi chiamare ignoreSnapshotOnNextApplicationLaunchper impedire che venga scattata l'immagine dell'istantanea.

Detto questo, sembra che l'istantanea dello schermo sia ancora scattata e quindi ho presentato una segnalazione di bug. Ma dovresti testare ulteriormente e vedere se l'uso di questa impostazione aiuta.

Se si trattava di un'app aziendale, potresti anche voler esaminare l'impostazione appropriata di allowScreenShotdelineata nella sezione Payload delle restrizioni del Riferimento al profilo di configurazione.


Ecco un'implementazione che raggiunge ciò di cui avevo bisogno. Puoi presentare il tuo UIImageViewoppure puoi utilizzare un modello di protocollo delegato per nascondere le informazioni riservate:

//  SecureDelegate.h

#import <Foundation/Foundation.h>

@protocol SecureDelegate <NSObject>

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

@end

Ho quindi dato alla mia app delegato una proprietà per questo:

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

Il mio controller di visualizzazione lo imposta:

- (void)viewDidLoad
{
    [super viewDidLoad];

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

Il view controller implementa ovviamente quel protocollo:

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

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

E, infine, il mio delegato dell'app si avvale di questo protocollo e proprietà:

- (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:"];
}

Nota, sto usando applicationWillResignActivepiuttosto che il consigliato applicationDidEnterBackground, perché, come altri hanno sottolineato, quest'ultimo non viene chiamato quando si tocca due volte il pulsante home mentre l'app è in esecuzione.

Vorrei poter utilizzare le notifiche per gestire tutto questo, piuttosto che il modello del protocollo delegato, ma nei miei test limitati, le notifiche non vengono gestite in modo abbastanza tempestivo, ma il modello sopra funziona bene.


6
Nota che, come accennato qui stackoverflow.com/questions/18937313/… il comportamento non è sempre lo stesso nel simulatore come sul dispositivo!
Michael Forrest

5
Per quel che vale, il ignoreSnapshotOnNextApplicationLaunchmetodo viene ignorato se non viene invocato durante il ripristino dello stato, come descritto qui .
Charles A.

3
Non è necessario farlo in applicationWillResignActive perché lo screenshot non viene acquisito fino a quando applicationDidEnterBackground. Quando tocchi due volte il pulsante Home, non viene acquisita alcuna schermata; lo schermo che stai vedendo è live. Aggiungere un'animazione allo schermo per verificare, ad esempio, scorrere i colori di sfondo. Se poi scegli un'altra app, la tua applicationDidEnterBackground verrà chiamata prima dello screenshot effettivo.
honus

3
A proposito, sono stato informato da un ingegnere Apple che la documentazione ignoreSnapshotOnNextApplicationLaunchnon è corretta, che, come tutti abbiamo osservato, viene effettivamente scattata un'istantanea, ma semplicemente non verrà utilizzata al prossimo lancio.
Rob il

2
ignoreSnapshotOnNextApplicationLaunch sembra fare quello che sembra: impedire che uno screenshot venga visualizzato durante l' avvio dell'applicazione . Non influisce sullo screenshot dell'interfaccia utente multitasking o se l'applicazione viene ripresa anziché avviata.
Glenn Maynard,

32

Questa è la soluzione con cui ho lavorato per la mia applicazione:

Come ha detto Tommy: puoi usare l'applicazioneWillResignActive. Quello che ho fatto è stato creare un UIImageView con la mia SplashImage e aggiungerlo come vista secondaria alla mia finestra principale-

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

Ho usato questo metodo al posto di applicationDidEnterBackground perché applicationDidEnterBackground non verrà attivato se tocchi due volte il pulsante home e applicationWillResignActive lo sarà. Ho sentito persone dire che può essere attivato anche in altri casi, quindi sto ancora testando in giro per vedere se dà problemi, ma finora nessuno! ;)

Qui per rimuovere la visualizzazione dell'immagine:

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

Spero che questo ti aiuti!

Nota a margine: l'ho testato sia sul simulatore che su un dispositivo reale: non verrà visualizzato sul simulatore, ma lo fa su un dispositivo reale!


2
C'è uno svantaggio: applicationWillResignActive viene chiamato, tra gli altri casi, quando l'utente abbassa il dock delle notifiche. Quindi fai scorrere il dito dall'alto verso il basso e vedi quell'immagine, mentre ti aspettavi di vedere i contenuti dell'app.
Kirill Gamazkov

4
È meglio sfocare le informazioni sensibili su applicationWillResignActive e impostare imageView su aplicationDidEnterBackground
Kirill Gamazkov

Questo funziona, ma c'è un bug rotazione quando distribuito a iOS7, costruita con xCode6: stackoverflow.com/questions/26147068/...
Alex Stone

Se scrivo il codice che hai menzionato in applicationWillResignActive , cosa vedrò quando riporterò la mia app in primo piano? Vedrò l'immagine "Ritratto (768x1024) .png" o lo schermo effettivo?
NSPratik

2
@Pratik: vedrai l'immagine, a meno che non la rimuovi di nuovo nel metodo applicationDidBecomeActive (vedi sopra).
Laura

14

Questo metodo semplice e veloce produrrà un'istantanea nera sopra l'icona della tua app nel commutatore di app iOS7 o successivo.

Innanzitutto, prendi la finestra della chiave della tua app (in genere configurata in AppDelegate.m nell'applicazione: didFinishLaunchingWithOptions) e nascondila quando la tua app sta per spostarsi in background:

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

Quindi, mostra la finestra della chiave della tua app quando la tua app diventa di nuovo attiva:

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

A questo punto, controlla il commutatore di app e verifica di vedere un'istantanea nera sopra l'icona della tua app. Ho notato che se avvii il commutatore di app immediatamente dopo aver spostato la tua app in background, potrebbe esserci un ritardo di ~ 5 secondi in cui vedrai un'istantanea della tua app (quella che vuoi nascondere!), Dopo che passa a un'istantanea completamente nera. Non sono sicuro di cosa sia successo con il ritardo; se qualcuno ha dei suggerimenti, si prega di intervenire.

Se vuoi un colore diverso dal nero nello switcher, puoi fare qualcosa di simile aggiungendo una sottoview con qualsiasi colore di sfondo che desideri:

- (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];
    }
}

Quindi, rimuovi questa sottoview di colore quando la tua app diventa di nuovo attiva:

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

4

Ho usato la seguente soluzione: quando l'applicazione sta per dimettersi ottengo un'istantanea di appWindow come vista e aggiungo sfocatura ad essa. Quindi aggiungo questa vista alla finestra dell'app

come fare questo:

  1. in appDelegate appena prima dell'implementazione aggiungi la riga:

    static const int kNVSBlurViewTag = 198490;//or wherever number you like
  2. aggiungi questi metodi:

    - (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. fai in modo che l'implementazione dell'appDelegate sia la seguente:

    - (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];
    }

Ho creato progetti di esempio in Swift e Objective C . Entrambi i progetti effettuano le seguenti azioni in:

-application: didResignActive: lo snapshot viene creato, sfocato e aggiunto alla finestra dell'app

-application: la visualizzazione sfocatura willBecomeActive viene rimossa dalla finestra.

Come usare:

Objecitve C

  1. Aggiungi AppDelegate+NVSBlurAppScreenfile .h e .m al tuo progetto

  2. nel tuo metodo -applicationWillResignActive: aggiungi la seguente riga:

    [self nvs_blurPresentedView];
  3. nel tuo metodo -applicationDidEnterBackground: aggiungi la seguente riga:

    [self nvs_unblurPresentedView];

Swift

  1. aggiungi il file AppDelegateExtention.swift al tuo progetto

  2. nella tua applicationWillResignActive aggiungi la seguente riga:

    blurPresentedView()
  3. nella funzione applicationDidBecomeActive aggiungere la seguente riga:

    unblurPresentedView()

2

se utilizzato solo [self.window addSubview:imageView];in applicationWillResignActivefunzione, questo imageView non coprirà UIAlertView, UIActionSheet o MFMailComposeViewController ...

La soluzione migliore è

- (void)applicationWillResignActive:(UIApplication *)application
{
    UIWindow *mainWindow = [[[UIApplication sharedApplication] windows] lastObject];
    [mainWindow addSubview:imageView];
}

Ottima soluzione se hai un video riprodotto in modalità a schermo intero in una visualizzazione web!
Tommie

Eccellente!. Funziona anche se abbiamo una tastiera aperta prima di entrare nel commutatore multitasking! Grazie
Prabhu.Somasundaram

0

Fornire la mia soluzione come "risposte", sebbene questa soluzione sia molto inaffidabile. A volte ottengo una schermata nera come screenshot, a volte XIB e talvolta uno screenshot dall'app stessa. A seconda del dispositivo e / o se lo eseguo nel simulatore.

Tieni presente che non posso fornire alcun codice per questa soluzione poiché contiene molti dettagli specifici dell'app. Ma questo dovrebbe spiegare l'essenza di base della mia soluzione.

In AppDelegate.m sotto applicationWillResignActive controllo se stiamo usando iOS7, se lo facciamo carico una nuova vista che è vuota con il logo dell'app al centro. Una volta che applicationDidBecomeActive si chiama, rilancio le mie vecchie visualizzazioni, che verranno ripristinate, ma funziona per il tipo di applicazione che sto sviluppando.


1
Se vedi cosa fa l'app Fotocamera - trasforma l'immagine in una sfocata quando passa in secondo piano (puoi vedere la transizione mentre si riduce all'icona)
Petesh,

@ Petesh Sì, è una bella implementazione, ma la domanda è come lo fanno. Temo un'API privata.
Rob il

@Tommie Tu dici "A seconda del dispositivo e / o se lo eseguo nel simulatore". Per me, sembra funzionare sul dispositivo (anche se non ho eseguito test esaustivi), ma non sul simulatore. Stai scoprendo che non funziona sul dispositivo?
Rob il

0

È possibile utilizzare l'attivatore per configurare il doppio clic del pulsante home per avviare il multitasking e disabilitare il doppio clic predefinito del pulsante home e l'avvio della finestra multitasking. Questo metodo può essere utilizzato per modificare gli screenshot nell'immagine predefinita dell'applicazione. Questo è applicabile alle app con funzione di protezione del passcode predefinita.


-2

Xamarin.iOS

Adattato da https://stackoverflow.com/a/20040270/7561

Invece di mostrare solo un colore, volevo mostrare la mia schermata di avvio.

 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();
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.