Controlando a captura de tela no switcher multitarefa iOS 7

98

Tenho tentado encontrar algumas informações sobre o novo switcher multitarefa no iOS 7 e especialmente a captura de tela que o sistema operacional faz quando o aplicativo está entrando em hibernação.

insira a descrição da imagem aqui

Existe alguma maneira de desligar completamente esse recurso ou captura de tela? Ou posso ocultar o aplicativo completamente do switcher? O aplicativo precisa ser executado em segundo plano, mas não queremos mostrar nenhuma captura de tela do aplicativo.

A captura de tela é potencialmente um risco de segurança, pense nas linhas dos aplicativos bancários em que o número do seu cartão ou o resumo da conta estarão disponíveis para qualquer pessoa que clicar duas vezes no botão home do dispositivo.

Alguém com alguma ideia sobre isso? Obrigado.

Tommie
fonte
1
Minha solução atual é carregar a visualização em branco com o logotipo do aplicativo assim que o aplicativo entrar em hibernação e removê-lo quando o aplicativo retornar, mas essa solução é difícil de gerenciar e parece mais um hack do que uma solução elegante. Além disso, esta solução falha de vez em quando, dependendo do dispositivo, etc. Portanto, não é realmente utilizável.
Tommie

Respostas:

101

Ao preparar sua IU para execução em segundo plano , a Apple diz:

Prepare sua IU para o instantâneo do aplicativo

Em algum ponto depois que seu aplicativo entra em segundo plano e seu método delegado retorna, o UIKit tira um instantâneo da interface de usuário atual do seu aplicativo. O sistema exibe a imagem resultante no alternador de aplicativos. Ele também exibe a imagem temporariamente ao trazer seu aplicativo de volta ao primeiro plano.

A IU do seu aplicativo não deve conter nenhuma informação sensível do usuário, como senhas ou números de cartão de crédito. Se sua interface contiver essas informações, remova-as de suas visualizações ao inserir o plano de fundo. Além disso, ignore alertas, interfaces temporárias e controladores de visualização do sistema que obscurecem o conteúdo do seu aplicativo. O instantâneo representa a interface do seu aplicativo e deve ser reconhecível pelos usuários. Quando seu aplicativo retornar ao primeiro plano, você pode restaurar dados e visualizações conforme apropriado.

Consulte Perguntas e respostas técnicas QA1838: Como evitar que informações confidenciais apareçam no alternador de tarefas

Além de ocultar / substituir informações confidenciais, você também pode pedir ao iOS 7 para não tirar o instantâneo da tela por meio de ignoreSnapshotOnNextApplicationLaunch, cuja documentação diz:

Se você achar que o instantâneo não pode refletir corretamente a interface do usuário do seu aplicativo quando o aplicativo for reiniciado, você pode chamar ignoreSnapshotOnNextApplicationLaunchpara evitar que a imagem do instantâneo seja obtida.

Dito isso, parece que o instantâneo da tela ainda foi tirado e, portanto, apresentei um relatório de bug. Mas você deve testar mais e ver se o uso dessa configuração ajuda.

Se este era um aplicativo corporativo, você também pode querer olhar para a configuração apropriada allowScreenShotdescrita na seção Restrictions Payload da Configuration Profile Reference.


Aqui está uma implementação que atinge o que eu precisava. Você pode apresentar o seu próprio UIImageViewou pode usar um padrão de protocolo de delegado para ocultar as informações confidenciais:

//  SecureDelegate.h

#import <Foundation/Foundation.h>

@protocol SecureDelegate <NSObject>

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

@end

Em seguida, dei ao meu delegado de aplicativo uma propriedade para isso:

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

Meu controlador de visualização define:

- (void)viewDidLoad
{
    [super viewDidLoad];

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

O controlador de visualização obviamente implementa esse protocolo:

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

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

E, finalmente, o delegado do meu aplicativo se vale deste protocolo e propriedade:

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

Observe, estou usando applicationWillResignActiveem vez do recomendado applicationDidEnterBackground, porque, como outros apontaram, este último não é chamado ao tocar duas vezes no botão home enquanto o aplicativo está em execução.

Eu gostaria de poder usar notificações para lidar com tudo isso, em vez do padrão de protocolo de delegado, mas em meus testes limitados, as notificações não são tratadas em tempo hábil o suficiente, mas o padrão acima funciona bem.

Roubar
fonte
6
Observe que, conforme mencionado aqui stackoverflow.com/questions/18937313/… o comportamento nem sempre é o mesmo no simulador e no dispositivo!
Michael Forrest,
5
Pelo que vale a pena, o ignoreSnapshotOnNextApplicationLaunchmétodo é ignorado se não for invocado durante a restauração do estado, conforme descrito aqui .
Charles A.
3
Você não precisa fazer isso em applicationWillResignActive porque a captura de tela não é tirada até applicationDidEnterBackground. Quando você dá um toque duplo no botão home, nenhuma captura de tela é tirada; a tela que você está vendo está ao vivo. Adicione uma animação à tela para verificar, por exemplo, cores de fundo do ciclo. Se você escolher outro aplicativo, seu applicationDidEnterBackground será chamado antes que a captura de tela real seja obtida.
honus de
3
Aliás, fui informado por um engenheiro da Apple que a documentação ignoreSnapshotOnNextApplicationLaunchestá incorreta, que, como todos observamos, um instantâneo foi realmente tirado, mas simplesmente não será usado no próximo lançamento.
Rob
2
ignoreSnapshotOnNextApplicationLaunch parece fazer o que parece: evitar que uma captura de tela seja exibida durante a inicialização do aplicativo . Isso não afeta a captura de tela da interface de usuário multitarefa ou se seu aplicativo está sendo retomado em vez de iniciado.
Glenn Maynard
32

Esta é a solução com a qual trabalhei para meu aplicativo:

Como disse Tommy: Você pode usar o applicationWillResignActive. O que fiz foi criar um UIImageView com meu SplashImage e adicioná-lo como subvisualização à minha janela principal-

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

Usei esse método em vez de applicationDidEnterBackground porque applicationDidEnterBackground não será acionado se você tocar duas vezes no botão home, e applicationWillResignActive será. Eu ouvi pessoas dizerem que pode ser acionado em outros casos também, então ainda estou testando para ver se dá problema, mas nenhum até agora! ;)

Aqui para remover a visualização da imagem:

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

Espero que isto ajude!

Sidenote: Eu testei isso tanto no simulador quanto em um dispositivo real: ele não vai mostrar no simulador, mas aparece em um dispositivo real!

Laura
fonte
2
Há uma desvantagem: applicationWillResignActive é chamado, entre outros casos, quando o usuário puxa para baixo o dock de notificações. Então você desliza o dedo de cima para baixo e vê a imagem, enquanto esperava ver o conteúdo do aplicativo.
Kirill Gamazkov de
4
É melhor desfocar informações confidenciais em applicationWillResignActive e definir imageView em aplicationDidEnterBackground
Kirill Gamazkov
Isso funciona, mas há um bug de rotação quando implantado no iOS7, criado com xCode6: stackoverflow.com/questions/26147068/…
Alex Stone
Se eu escrever o código que você mencionou em applicationWillResignActive , o que verei quando colocar meu aplicativo de volta no estado de primeiro plano? Verei a imagem "Retrato (768x1024) .png" ou a tela real?
NSPratik
2
@Pratik: Você verá a imagem, a menos que a remova novamente no método applicationDidBecomeActive (veja acima).
Laura
14

Este método rápido e fácil produzirá um instantâneo preto acima do ícone do seu aplicativo no iOS7 ou posterior alternador de aplicativos.

Primeiro, pegue a janela principal de seu aplicativo (normalmente configurada em AppDelegate.m em application: didFinishLaunchingWithOptions) e oculte-a quando seu aplicativo estiver prestes a passar para o segundo plano:

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

Em seguida, revele a janela principal do seu aplicativo quando ele se tornar ativo novamente:

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

Neste ponto, verifique o alternador de aplicativos e verifique se você vê um instantâneo preto acima do ícone do seu aplicativo. Percebi que se você iniciar o alternador de aplicativos imediatamente após mover seu aplicativo para o segundo plano, pode haver um atraso de cerca de 5 segundos onde você verá um instantâneo do seu aplicativo (aquele que você deseja ocultar!), Após que faz a transição para um instantâneo totalmente preto. Não tenho certeza do que está acontecendo com o atraso; se alguém tiver alguma sugestão, por favor, entre em contato.

Se quiser uma cor diferente de preto no switcher, você pode fazer algo assim adicionando uma subvisualização com qualquer cor de fundo de sua preferência:

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

Em seguida, remova esta subvisão de cor quando seu aplicativo ficar ativo novamente:

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

Usei a seguinte solução: quando o aplicativo vai encerrar, pego o instantâneo do appWindow como uma Visualização e adiciono desfoque a ele. Então, adiciono esta visualização à janela do aplicativo

como fazer isso:

  1. em appDelegate antes da linha de adição de implementação:

    static const int kNVSBlurViewTag = 198490;//or wherever number you like
  2. adicione estes 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. faça com que sua implementação appDelegate seja a seguinte:

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

Criei projectos exemplo na Swift e Objectivo C . Ambos os projetos realizam as seguintes ações em:

-application: didResignActive - o instantâneo é criado, desfocado e adicionado à janela do aplicativo

-application: a visualização de desfoque willBecomeActive está sendo removida da janela.

Como usar:

Objecitve C

  1. Adicione AppDelegate+NVSBlurAppScreenarquivos .h e .m ao seu projeto

  2. em seu método -applicationWillResignActive: adicione a seguinte linha:

    [self nvs_blurPresentedView];
  3. em seu método -applicationDidEnterBackground: adicione a seguinte linha:

    [self nvs_unblurPresentedView];

Rápido

  1. adicione o arquivo AppDelegateExtention.swift ao seu projeto

  2. na função applicationWillResignActive, adicione a seguinte linha:

    blurPresentedView()
  3. na função applicationDidBecomeActive, adicione a seguinte linha:

    unblurPresentedView()
Nikolay Shubenkov
fonte
2

se for usado apenas [self.window addSubview:imageView];em applicationWillResignActivefunção, este imageView não cobrirá UIAlertView, UIActionSheet ou MFMailComposeViewController ...

Melhor solução é

- (void)applicationWillResignActive:(UIApplication *)application
{
    UIWindow *mainWindow = [[[UIApplication sharedApplication] windows] lastObject];
    [mainWindow addSubview:imageView];
}
Zorot
fonte
Uma solução muito boa se você tiver um vídeo sendo reproduzido em modo de tela cheia em uma visualização da web!
Tommie,
Excelente!. Funciona mesmo se tivermos um teclado aberto antes de entrar no switcher multitarefa! Obrigado
Prabhu.Somasundaram
0

Fornecendo minha própria solução como "respostas", embora essa solução não seja muito confiável. Às vezes recebo uma tela preta como a captura de tela, às vezes o XIB e às vezes uma captura de tela do próprio aplicativo. Dependendo do dispositivo e / ou se eu executar isso no simulador.

Observe que não posso fornecer nenhum código para esta solução, pois contém muitos detalhes específicos do aplicativo. Mas isso deve explicar a essência da minha solução.

Em AppDelegate.m em applicationWillResignActive, verifico se estamos executando o iOS7, se fizermos isso carrego uma nova visualização que está vazia com o logotipo do aplicativo no meio. Assim que applicationDidBecomeActive for chamado, eu relançar minhas visualizações antigas, que serão redefinidas - mas isso funciona para o tipo de aplicativo que estou desenvolvendo.

Tommie
fonte
1
Se você vir o que o aplicativo da câmera faz - ele faz a transição da imagem para uma desfocada ao entrar no plano de fundo (você pode ver a transição quando ela se reduz ao ícone)
Petesh
@Petesh Sim, é uma boa implementação, mas a questão é como eles fazem isso. Temo uma API privada.
Rob
@Tommie Você diz "Dependendo do dispositivo e / ou se eu executar isso no simulador". Para mim, parece funcionar no dispositivo (embora eu não tenha feito testes exaustivos), mas não no simulador. Você está achando que não funciona no dispositivo?
Rob
0

Você pode usar o ativador para configurar o clique duplo do botão home para iniciar a multitarefa e desabilitar o clique duplo padrão do botão home e iniciar a janela multitarefa. Este método pode ser usado para alterar as capturas de tela para a imagem padrão do aplicativo. Isso se aplica a aplicativos com recurso de proteção por senha padrão.

Fareed.MK
fonte
-2

Xamarin.iOS

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

Em vez de apenas mostrar uma cor, eu queria mostrar minha tela de inicialização.

 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
fonte