Управление снимком экрана в переключателе многозадачности iOS 7

98

Я пытался найти некоторую информацию о новом переключателе многозадачности в iOS 7 и особенно о снимке экрана, который ОС делает, когда приложение переходит в режим гибернации.

введите описание изображения здесь

Есть ли способ полностью отключить эту функцию или снимок экрана? Или можно вообще скрыть приложение от переключателя? Приложение должно работать в фоновом режиме, но мы не хотим показывать снимки экрана из приложения.

Снимок экрана потенциально представляет собой угрозу безопасности, подумайте также о банковских приложениях, где номер вашей карты или сводка по счету будут доступны любому, кто дважды щелкнет кнопку «Домой» на устройстве.

Кто-нибудь знает об этом? Спасибо.

Томми
источник
1
Мое текущее решение - загрузить пустое представление с логотипом приложения после того, как приложение перейдет в режим гибернации, и удалить его, когда приложение вернется, но этим решением сложно управлять и оно похоже на взлом, а не на элегантное решение. Кроме того, это решение время от времени дает сбой, в зависимости от устройства и т. Д. Так что его нельзя использовать.
Tommie

Ответы:

101

В разделе «Подготовка пользовательского интерфейса к работе в фоновом режиме» Apple говорит:

Подготовьте свой пользовательский интерфейс для снимка приложения

В какой-то момент после того, как ваше приложение переходит в фоновый режим и возвращается ваш метод делегата, UIKit делает снимок текущего пользовательского интерфейса вашего приложения. Система отображает получившееся изображение в переключателе приложений. Он также временно отображает изображение при возврате вашего приложения на передний план.

Пользовательский интерфейс вашего приложения не должен содержать конфиденциальную информацию о пользователе, например пароли или номера кредитных карт. Если ваш интерфейс содержит такую ​​информацию, удалите ее из окон при входе в фоновый режим. Кроме того, отклоните предупреждения, временные интерфейсы и контроллеры системного представления, которые скрывают содержимое вашего приложения. Снимок представляет интерфейс вашего приложения и должен быть узнаваем для пользователей. Когда ваше приложение возвращается на передний план, вы можете восстанавливать данные и представления по мере необходимости.

См. Технические вопросы и ответы QA1838: Предотвращение появления конфиденциальной информации в переключателе задач

В дополнение к скрытию / замене конфиденциальной информации вы также можете указать iOS 7 не делать снимок экрана через ignoreSnapshotOnNextApplicationLaunch, чья документация гласит:

Если вы чувствуете, что моментальный снимок не может правильно отражать пользовательский интерфейс вашего приложения при его перезапуске, вы можете позвонить, ignoreSnapshotOnNextApplicationLaunchчтобы предотвратить создание этого снимка.

Сказав это, похоже, что снимок экрана все еще сделан, и поэтому я отправил отчет об ошибке. Но вам следует продолжить тестирование и посмотреть, помогает ли использование этого параметра.

Если это приложение предприятие, вы также можете посмотреть в соответствующей обстановке allowScreenShotизложенных в Ограничения Payload разделе Configuration Profile Reference.


Вот реализация, которая дает то, что мне нужно. Вы можете представить свой собственный UIImageViewили использовать шаблон делегированного протокола, чтобы скрыть конфиденциальную информацию:

//  SecureDelegate.h

#import <Foundation/Foundation.h>

@protocol SecureDelegate <NSObject>

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

@end

Затем я дал своему делегату приложения свойство для этого:

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

Мой контроллер представления устанавливает его:

- (void)viewDidLoad
{
    [super viewDidLoad];

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

Контроллер представления, очевидно, реализует этот протокол:

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

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

И, наконец, делегат моего приложения использует этот протокол и свойство:

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

Обратите внимание: я использую, applicationWillResignActiveа не рекомендованный applicationDidEnterBackground, потому что, как указывали другие, последний не вызывается при двойном нажатии на кнопку «Домой» во время работы приложения.

Я хотел бы использовать уведомления для обработки всего этого, а не шаблон делегат-протокол, но в моем ограниченном тестировании уведомления не обрабатываются достаточно своевременно, но вышеуказанный шаблон работает нормально.

Роб
источник
6
Обратите внимание, что, как упоминалось здесь stackoverflow.com/questions/18937313/…, поведение в симуляторе не всегда такое же, как на устройстве!
Майкл Форрест
5
Как бы то ни было, ignoreSnapshotOnNextApplicationLaunchметод игнорируется, если он не вызывается во время восстановления состояния, как описано здесь .
Charles A.
3
Вам не нужно делать это в applicationWillResignActive, потому что снимок экрана не делается до applicationDidEnterBackground. При двойном нажатии кнопки «Домой» снимок экрана не делается; экран, который вы видите, активен. Добавьте анимацию на экран для проверки, например, измените цвет фона. Если вы затем выберете другое приложение, ваше applicationDidEnterBackground будет вызвано до того, как будет сделан фактический снимок экрана.
honus
3
Кстати, инженер Apple проинформировал меня, что документация ignoreSnapshotOnNextApplicationLaunchневерна, что, как мы все заметили, моментальный снимок действительно сделан, но просто не будет использоваться при следующем запуске.
Роб
2
ignoreSnapshotOnNextApplicationLaunch, похоже, делает то, на что похоже: предотвращает отображение снимка экрана во время запуска приложения . Это не влияет на снимок экрана многозадачного пользовательского интерфейса или на то, что ваше приложение возобновляется, а не запускается.
Гленн Мейнард
32

Это решение, с которым я работал для своего приложения:

Как сказал Томми: вы можете использовать applicationWillResignActive. Что я сделал, так это сделал UIImageView с моим SplashImage и добавил его как subview в мое главное окно -

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

Я использовал этот метод вместо applicationDidEnterBackground, потому что applicationDidEnterBackground не сработает, если вы дважды нажмете кнопку «Домой», а applicationWillResignActive будет. Я слышал, как люди говорили, что он может срабатывать и в других случаях, поэтому я все еще проверяю, есть ли проблемы, но пока нет! ;)

Здесь, чтобы удалить изображение:

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

Надеюсь это поможет!

Примечание: я тестировал это как на симуляторе, так и на реальном устройстве: он не будет отображаться на симуляторе, но на реальном устройстве!

Лаура
источник
2
Есть один недостаток: applicationWillResignActive, среди прочего, вызывается, когда пользователь открывает панель уведомлений. Итак, вы проводите пальцем сверху вниз и видите это изображение, в то время как вы ожидали увидеть содержимое приложения.
Кирилл Гамазков
4
Лучше размыть конфиденциальную информацию в applicationWillResignActive и установить imageView в aplicationDidEnterBackground
Кирилл Гамазков
Это работает, но при развертывании на iOS7, построенном с помощью xCode6, возникает ошибка вращения: stackoverflow.com/questions/26147068/…
Alex Stone
Если я напишу код, который вы упомянули в applicationWillResignActive , то что я увижу, когда верну свое приложение в состояние переднего плана? Увижу ли я изображение «Портрет (768x1024) .png» или настоящий экран?
NSPratik 03
2
@Pratik: вы увидите изображение, если не удалите его снова в методе applicationDidBecomeActive (см. Выше).
Лаура
14

Этот быстрый и простой метод позволит получить черный снимок над значком вашего приложения в переключателе приложений iOS7 или более поздней версии.

Сначала возьмите ключевое окно вашего приложения (обычно настраивается в AppDelegate.m в application: didFinishLaunchingWithOptions) и скройте его, когда ваше приложение собирается перейти в фоновый режим:

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

Затем отмените скрытие ключевого окна вашего приложения, когда ваше приложение снова станет активным:

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

На этом этапе проверьте переключатель приложений и убедитесь, что вы видите черный снимок над значком вашего приложения. Я заметил, что если вы запустите переключатель приложений сразу после перемещения приложения в фоновый режим, может возникнуть задержка примерно на 5 секунд, когда вы увидите снимок своего приложения (тот, который вы хотите скрыть!), После который переходит в полностью черный снимок. Я не уверен, что случилось с задержкой; если у кого-то есть предложения, пожалуйста, свяжитесь с нами.

Если вы хотите, чтобы в переключателе был другой цвет, а не черный, вы можете сделать что-то вроде этого, добавив подпредставление с любым цветом фона, который вам нужен:

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

Затем удалите это представление цвета, когда ваше приложение снова станет активным:

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    if(isIOS7Or8)
    {
        UIView *colorView = [self.window viewWithTag:9999];
        [colorView removeFromSuperview];
    }
}
Джон Ячеко
источник
4

Я использовал следующее решение: когда приложение собирается уволиться, я получаю снимок appWindow как View и добавляю к нему размытие. Затем я добавляю это представление в окно приложения

как это сделать:

  1. в appDelegate непосредственно перед реализацией добавьте строку:

    static const int kNVSBlurViewTag = 198490;//or wherever number you like
  2. добавьте эти методы:

    - (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. сделайте вашу реализацию appDelegate следующим образом:

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

Я создал Пример проектов в Swift и Objective C . Оба проекта производят следующие действия в:

-application: didResignActive - снимок создается, размывается и добавляется в окно приложения

-application: willBecomeActive вид размытия удаляется из окна.

Как пользоваться:

Objecitve C

  1. Добавьте AppDelegate+NVSBlurAppScreenв проект файлы .h и .m

  2. в вашем методе -applicationWillResignActive: добавьте следующую строку:

    [self nvs_blurPresentedView];
  3. в вашем методе -applicationDidEnterBackground: добавьте следующую строку:

    [self nvs_unblurPresentedView];

Swift

  1. добавьте файл AppDelegateExtention.swift в свой проект

  2. в вашей функции applicationWillResignActive добавьте следующую строку:

    blurPresentedView()
  3. в вашей функции applicationDidBecomeActive добавьте следующую строку:

    unblurPresentedView()
Николай Шубенков
источник
2

если используется только [self.window addSubview:imageView];в applicationWillResignActiveфункции, этот imageView не будет охватывать UIAlertView, UIActionSheet или MFMailComposeViewController ...

Лучшее решение

- (void)applicationWillResignActive:(UIApplication *)application
{
    UIWindow *mainWindow = [[[UIApplication sharedApplication] windows] lastObject];
    [mainWindow addSubview:imageView];
}
Зорот
источник
Очень хорошее решение, если видео воспроизводится в полноэкранном режиме в веб-просмотре!
Tommie
Превосходно!. Он работает, даже если перед переключателем многозадачности у нас открыта клавиатура! Спасибо
Прабху Сомасундарам
0

Предлагаю свое собственное решение в качестве «ответов», хотя это решение очень ненадежно. Иногда в качестве снимка экрана отображается черный экран, иногда - XIB, а иногда - снимок экрана самого приложения. В зависимости от устройства и / или запускаю это в симуляторе.

Обратите внимание, что я не могу предоставить код для этого решения, так как там много деталей, специфичных для приложения. Но это должно объяснить основную суть моего решения.

В AppDelegate.m в разделе applicationWillResignActive я проверяю, работаем ли мы под управлением iOS7, загружаю ли мы новое представление, пустое с логотипом приложения посередине. После вызова applicationDidBecomeActive я повторно запускаю свои старые представления, которые будут сброшены, но это работает для того типа приложения, которое я разрабатываю.

Томми
источник
1
Если вы видите, что делает приложение «Камера» - оно преобразует изображение в размытое при переходе в фоновый режим (вы можете видеть переход, когда он уменьшается до значка)
Питеш
@Petesh Да, это хорошая реализация, но вопрос в том, как они это делают. Боюсь частного API.
Роб
@Tommie Вы говорите: «В зависимости от устройства и / или запускаю это в симуляторе». Для меня это кажется работать на устройстве (хотя я не сделал исчерпывающее тестирование), но не симулятор. Вы обнаружите, что это не работает на устройстве?
Роб
0

Вы можете использовать активатор для настройки двойного нажатия кнопки «Домой» для запуска многозадачности и отключения двойного нажатия кнопки «Домой» по умолчанию и запуска окна многозадачности. Этот метод можно использовать для замены снимков экрана на изображение по умолчанию для приложения. Это применимо к приложениям с функцией защиты паролем по умолчанию.

Fareed.MK
источник
-2

Xamarin.iOS

Взято из https://stackoverflow.com/a/20040270/7561

Вместо того, чтобы просто показывать цвет, я хотел показать свой стартовый экран.

 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();
}
Бен
источник