Керування скріншотом у багатозадачному комутаторі iOS 7


98

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

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

Чи є спосіб повністю вимкнути цю функцію чи скріншот? Або я можу повністю приховати додаток від комутатора? Додаток потрібно запускати у фоновому режимі, але ми не хочемо показувати скріншот із програми.

Скріншот - це потенційно загроза безпеці. Подумайте, що для банківських додатків потрібно додати номер, де номер вашої картки чи резюме рахунку будуть доступні для кожного, хто двічі клацне на додому кнопку на пристрої.

Хтось із цим розуміє? Дякую.


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

Відповіді:


101

В Підготовка інтерфейсу для роботи у фоновому режимі , Apple говорить:

Підготуйте інтерфейс користувача до знімка програми

Через деякий час після того, як ваша програма перейде на задній план і ваш метод делегації повернеться, UIKit робить знімок поточного користувальницького інтерфейсу вашої програми. Система відображає отримане зображення в перемикачі програм. Він також відображає зображення тимчасово, коли ваш додаток повертається на перший план.

Інтерфейс вашого додатка не повинен містити конфіденційну інформацію користувачів, наприклад паролі чи номери кредитних карт. Якщо ваш інтерфейс містить таку інформацію, видаліть її зі своїх поглядів під час виходу на задній план. Крім того, відхиліть сповіщення, тимчасові інтерфейси та контролери перегляду системи, які затемнюють вміст вашої програми. Знімок відображає інтерфейс вашої програми та має бути розпізнаваним для користувачів. Коли ваша програма повернеться на перший план, ви можете відновити дані та представлення даних, якщо це доречно.

Див. Технічні питання QA1838: Запобігання появі чутливої ​​інформації у перемикачі завдань

На додаток до затемнення / заміни конфіденційної інформації, ви також можете сказати iOS 7 не робити знімок екрана ignoreSnapshotOnNextApplicationLaunch, в документації якого написано:

Якщо ви вважаєте, що знімок не може правильно відображати користувальницький інтерфейс вашого додатка, коли програма перезапущена, ви можете зателефонувати, ignoreSnapshotOnNextApplicationLaunchщоб запобігти зняттю цього знімку.

Сказавши це, виявляється, що знімок екрана все ще зроблений, і тому я подав звіт про помилку. Але слід перевірити далі і побачити, чи допоможе використання цього параметра.

Якщо це був корпоративний додаток, ви також можете вивчити відповідне налаштування, allowScreenShotвикладене в розділі Обмеження корисної навантаження в довідці профілю конфігурації.


Ось реалізація, яка досягає того, що мені було потрібно. Ви можете представити свій власний 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метод ігнорується, якщо він не викликається під час відновлення стану, як описано тут .
Чарльз А.

3
Вам не потрібно робити це в applicationWillResignActive, оскільки знімок екрана не приймається до applicationDidEnterBackground. Якщо двічі торкнутись кнопки будинку, знімок екрана не робиться; екран, який ви бачите, наживо. Додайте анімацію на екран, щоб перевірити, наприклад, циклічні кольори тла. Якщо ви виберете інший додаток, ваша програмаDidEnterBackground буде викликана до того, як буде зроблено фактичний знімок екрана.
хону

3
До речі, мені інженер Apple повідомив, що документація ignoreSnapshotOnNextApplicationLaunchневірна, що, як ми всі зауважили, знімок насправді зроблений, але він просто не буде використаний при наступному запуску.
Роб

2
ignoreSnapshotOnNextApplicationLaunch, здається, робить те, що це звучить: запобігає показу екрана під час запуску програми . Це не впливає на багатозадачний скріншот інтерфейсу користувача або якщо ваша програма відновлюється, а не запускається.
Глен Мейнард

32

Це рішення, з яким я працював для своєї заявки:

Як сказав Томмі: Ви можете використовувати додатокWillResignActive. Що я зробив, це створити UIImageView за допомогою мого SplashImage та додати його як підпогляд до мого головного вікна,

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

Сподіваюся, це допомагає!

Sidenote: Я перевірив це як на тренажері, так і на реальному пристрої: це не відображатиметься на тренажері, але на реальному пристрої!


2
Є один недолік: серед інших випадків викликається applicationWillResignActive, коли користувач спускає док-станцію сповіщень. Таким чином, ви проведіть пальцем зверху вниз і побачите це зображення, тоді як ви очікували побачити вміст програми.
Кирило Гамазков

4
Краще розмити конфіденційну інформацію про додатокWillResignActive та встановити imageView on aplicationDidEnterBackground
Кирило Гамазков

Це працює, але помилка обертання під час розгортання до iOS7, побудованого за допомогою xCode6: stackoverflow.com/questions/26147068 / ...
Alex Stone

Якщо я напишу код, який ви згадали у applicationWillResignActive , то що я побачу, коли поверну свою програму у стан переднього плану? Чи побачу зображення "Портрет (768x1024) .png" або власне екран?
NSPratik

2
@Pratik: Ви побачите зображення, якщо ви його знову не видалите в методі applicationDidBecomeActive (див. Вище).
Лаура

14

Цей швидкий і простий метод дасть чорний знімок над піктограмою вашого додатка в перемикачі програм iOS7 або пізнішої версії.

Спочатку візьміть ключове вікно програми (як правило, встановіть у AppDelegate.m в додатку: 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 як перегляд та додаю до нього розмиття. Потім я додаю цей погляд у вікно програми

як це зробити:

  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 - знімок створюється, розмивається та додається до вікна програми

-застосування: willBecomeActive розмиття подання видаляється з вікна.

Як використовувати:

Objecitve C

  1. Додайте AppDelegate+NVSBlurAppScreenдо свого проекту файли .h та .m

  2. у вашому методі -applicationWillResignActive: додайте наступний рядок:

    [self nvs_blurPresentedView];
  3. у вашому -applicationDidEnterBackground: методі додайте наступний рядок:

    [self nvs_unblurPresentedView];

Швидкий

  1. додати файл AppDelegateExtention.swift до свого проекту

  2. у своїй програміWillResignActive додайте наступний рядок:

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

Дуже хороше рішення, якщо у веб-перегляді є відео в повноекранному режимі!
Томмі

Відмінно !. Він працює, навіть якщо перед відкриттям багатозадачного перемикача у нас відкрита клавіатура! Спасибі
Prabhu.Somasundaram

0

Надання власного рішення як "відповіді", хоча це рішення є дуже ненадійним. Іноді я отримую чорний екран як скріншот, іноді XIB, а іноді скріншот із самого додатка. Залежно від пристрою та / або якщо я запускаю це в тренажері.

Зауважте, що я не можу надати жодного коду для цього рішення, оскільки там багато деталей щодо додатків. Але це повинно пояснити основну суть мого рішення.

У AppDelegate.m під applicationWillResignActive я перевіряю, чи працює ми iOS7, чи завантажуємо новий вигляд, порожній із логотипом програми посередині. Після виклику applicationDidBecomeActive я повторно запускаю свої старі представлення, які будуть скинуті - але це працює для типу програми, яку я розробляю.


1
Якщо ви бачите, що робить додаток Camera - воно перетворює зображення в розмите, коли ви переходите на задній план (ви можете бачити перехід, як він зменшується до значка)
Petesh

@ Petesh Так, це приємна реалізація, але питання полягає в тому, як вони це роблять. Я боюся приватного API.
Роб

@Tommie Ви говорите "Залежно від пристрою та / або якщо я запускаю це в тренажері". Для мене, здається , він працює на пристрої (хоча я не робив вичерпних тестів), але не на тренажері. Ви вважаєте, що це не працює на пристрої?
Роб

0

Ви можете використовувати активатор для налаштування подвійного клацання кнопки додому, щоб запустити багатозадачність та відключити подвійне клацання кнопки додому та запуск вікна багатозадачності. Цей метод можна використовувати для зміни знімків екрана на зображення за замовчуванням програми. Це стосується програм із функцією захисту пароля за замовчуванням.


-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();
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.