Sterowanie zrzutem ekranu w przełączniku wielozadaniowym iOS 7

98

Próbowałem znaleźć informacje dotyczące nowego przełącznika wielozadaniowego w iOS 7, a zwłaszcza zrzut ekranu, który system operacyjny wykonuje, gdy aplikacja przechodzi w stan hibernacji.

wprowadź opis obrazu tutaj

Czy istnieje sposób, aby całkowicie wyłączyć tę funkcję lub zrzut ekranu? Czy mogę całkowicie ukryć aplikację przed przełącznikiem? Aplikacja musi działać w tle, ale nie chcemy wyświetlać żadnego zrzutu ekranu z aplikacji.

Zrzut ekranu stanowi potencjalne zagrożenie bezpieczeństwa, pomyśl podobnie jak w przypadku aplikacji bankowych, w których numer Twojej karty lub podsumowanie konta będą dostępne dla każdego, kto dwukrotnie kliknie przycisk strony głównej na urządzeniu.

Czy ktoś ma w tym jakiś wgląd? Dzięki.

Tommie
źródło
1
Moje obecne rozwiązanie polega na załadowaniu pustego widoku z logo aplikacji po przejściu aplikacji w stan hibernacji i usunięciu go, gdy aplikacja powraca, ale to rozwiązanie jest trudne w zarządzaniu i wydaje się raczej hakowaniem niż eleganckim rozwiązaniem. Ponadto, to rozwiązanie czasami zawodzi, w zależności od urządzenia itp. Więc tak naprawdę nie jest użyteczne.
Tommie,

Odpowiedzi:

101

W Przygotowanie UI aby działać w tle , Apple mówi:

Przygotuj swój interfejs użytkownika na migawkę aplikacji

W pewnym momencie po tym, jak aplikacja przejdzie w tło i powróci metoda delegata, UIKit wykonuje migawkę bieżącego interfejsu użytkownika aplikacji. System wyświetla wynikowy obraz w przełączniku aplikacji. Wyświetla również obraz tymczasowo podczas przenoszenia aplikacji z powrotem na pierwszy plan.

Interfejs użytkownika aplikacji nie może zawierać żadnych poufnych informacji o użytkowniku, takich jak hasła czy numery kart kredytowych. Jeśli twój interfejs zawiera takie informacje, usuń je ze swoich widoków podczas przechodzenia w tło. Odrzuć także alerty, tymczasowe interfejsy i kontrolery widoku systemu, które zasłaniają zawartość aplikacji. Migawka przedstawia interfejs aplikacji i powinna być rozpoznawalna dla użytkowników. Gdy aplikacja wróci na pierwszy plan, możesz odpowiednio przywrócić dane i widoki.

Widzieć Pytania i odpowiedzi techniczne QA1838: Zapobieganie pojawianiu się poufnych informacji w przełączniku zadań

Oprócz ukrywania / zastępowania poufnych informacji możesz również powiedzieć systemowi iOS 7, aby nie wykonywał migawki ekranu za pośrednictwem ignoreSnapshotOnNextApplicationLaunch, którego dokumentacja mówi:

Jeśli uważasz, że migawka nie może poprawnie odzwierciedlać interfejsu użytkownika aplikacji po ponownym uruchomieniu aplikacji, możesz zadzwonić, ignoreSnapshotOnNextApplicationLaunchaby zapobiec wykonaniu tego obrazu migawki.

To powiedziawszy, wygląda na to, że zrzut ekranu jest nadal wykonywany i dlatego złożyłem raport o błędzie. Ale powinieneś przetestować dalej i sprawdzić, czy użycie tego ustawienia pomaga.

Jeśli była to aplikacja dla przedsiębiorstw, warto również przyjrzeć się odpowiedniemu ustawieniu allowScreenShotopisanemu w sekcji Obciążenie ograniczeń w dokumencie Odwołanie do profilu konfiguracji.


Oto implementacja, która osiąga to, czego potrzebowałem. Możesz przedstawić własny UIImageViewwzorzec lub użyć wzorca protokołu delegata, aby ukryć poufne informacje:

//  SecureDelegate.h

#import <Foundation/Foundation.h>

@protocol SecureDelegate <NSObject>

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

@end

Następnie przekazałem delegatowi mojej aplikacji właściwość:

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

Mój kontroler widoku ustawia to:

- (void)viewDidLoad
{
    [super viewDidLoad];

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

Kontroler widoku oczywiście implementuje ten protokół:

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

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

I wreszcie, mój delegat aplikacji korzysta z tego protokołu i właściwości:

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

Uwaga, używam applicationWillResignActiveraczej niż zalecanaapplicationDidEnterBackground , ponieważ, jak zauważyli inni, ten ostatni nie jest wywoływany po dwukrotnym dotknięciu przycisku głównego, gdy aplikacja jest uruchomiona.

Chciałbym móc używać powiadomień do obsługi tego wszystkiego, zamiast wzorca delegata-protokołu, ale w moich ograniczonych testach powiadomienia nie są obsługiwane w odpowiednim czasie, ale powyższy wzorzec działa dobrze.

Obrabować
źródło
6
Zauważ, że jak wspomniano tutaj stackoverflow.com/questions/18937313/ ... zachowanie nie zawsze jest takie samo w symulatorze jak na urządzeniu!
Michael Forrest,
5
Co jest warte, ignoreSnapshotOnNextApplicationLaunchmetoda jest ignorowana, jeśli nie zostanie wywołana podczas przywracania stanu, jak opisano tutaj .
Charles A.
3
Nie musisz tego robić w applicationWillResignActive, ponieważ zrzut ekranu jest wykonywany dopiero po applicationDidEnterBackground. Po dwukrotnym dotknięciu przycisku strony głównej nie jest wykonywany żaden zrzut ekranu; ekran, który widzisz, jest na żywo. Dodaj animację do swojego ekranu, aby zweryfikować, np. Cykliczne kolory tła. Jeśli następnie wybierzesz inną aplikację, twoja applicationDidEnterBackground zostanie wywołana przed wykonaniem rzeczywistego zrzutu ekranu.
honus
3
Przy okazji, inżynier Apple poinformował mnie, że dokumentacja ignoreSnapshotOnNextApplicationLaunchjest niepoprawna, że ​​jak wszyscy zauważyliśmy, migawka jest faktycznie wykonywana, ale po prostu nie zostanie użyta przy następnym uruchomieniu.
Rob
2
ignoreSnapshotOnNextApplicationLaunch wydaje się robić to, na co wygląda: zapobiegać wyświetlaniu zrzutu ekranu podczas uruchamiania aplikacji . Nie ma to wpływu na wielozadaniowy zrzut ekranu interfejsu użytkownika ani na wznowienie, a nie uruchomienie aplikacji.
Glenn Maynard
32

Oto rozwiązanie, z którym pracowałem dla mojej aplikacji:

Jak powiedział Tommy: Możesz użyć applicationWillResignActive. To, co zrobiłem, to utworzenie UIImageView z moim SplashImage i dodanie go jako podwidoku do mojego głównego okna-

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

Użyłem tej metody zamiast applicationDidEnterBackground, ponieważ applicationDidEnterBackground nie zostanie wyzwolona, ​​jeśli dwukrotnie klikniesz przycisk home, a applicationWillResignActive będzie. Słyszałem, jak ludzie mówią, że może to być wyzwalane również w innych przypadkach, więc nadal testuję, aby sprawdzić, czy powoduje to problem, ale jak dotąd żaden! ;)

Tutaj, aby usunąć widok obrazu:

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

Mam nadzieję że to pomoże!

Uwaga: Przetestowałem to zarówno na symulatorze, jak i na prawdziwym urządzeniu: nie pokaże się na symulatorze, ale na prawdziwym urządzeniu!

Laura
źródło
2
Jest jedna wada: applicationWillResignActive jest wywoływana, między innymi, gdy użytkownik otwiera pasek powiadomień. Przesuwasz więc palcem od góry do dołu i widzisz ten obraz, podczas gdy spodziewasz się zobaczyć zawartość aplikacji.
Kirill Gamazkov
4
Lepiej jest zamazać poufne informacje w applicationWillResignActive i ustawić imageView na aplicationDidEnterBackground
Kirill Gamazkov
To działa, ale występuje błąd rotacji po wdrożeniu na iOS7, zbudowany z xCode6: stackoverflow.com/questions/26147068/ ...
Alex Stone
Jeśli napiszę kod, o którym wspomniałeś w applicationWillResignActive , co zobaczę, gdy przywrócę moją aplikację do stanu pierwszego planu? Czy zobaczę obraz „Portret (768x1024) .png” czy rzeczywisty ekran?
NSPratik
2
@Pratik: Zobaczysz obraz, chyba że usuniesz go ponownie w metodzie applicationDidBecomeActive (patrz wyżej).
Laura,
14

Ta szybka i łatwa metoda da czarną migawkę nad ikoną aplikacji w przełączniku aplikacji iOS7 lub nowszym.

Najpierw weź okno klucza aplikacji (zwykle jest to konfigurowane w AppDelegate.m w application: didFinishLaunchingWithOptions) i ukryj je, gdy aplikacja ma zamiar przejść do tła:

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

Następnie usuń ukrywanie okna klucza aplikacji, gdy aplikacja stanie się ponownie aktywna:

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

W tym momencie sprawdź przełącznik aplikacji i sprawdź, czy widzisz czarną migawkę nad ikoną aplikacji. Zauważyłem, że jeśli uruchomisz przełącznik aplikacji natychmiast po przeniesieniu aplikacji w tło, może wystąpić opóźnienie ~ 5 sekund, w którym zobaczysz migawkę aplikacji (tę, którą chcesz ukryć!), Po który przechodzi w całkowicie czarną migawkę. Nie jestem pewien, o co chodzi z opóźnieniem; jeśli ktoś ma jakieś sugestie, zadzwoń.

Jeśli chcesz mieć w przełączniku kolor inny niż czarny, możesz zrobić coś takiego, dodając widok podrzędny z dowolnym kolorem tła:

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

Następnie usuń ten widok podrzędny koloru, gdy aplikacja stanie się ponownie aktywna:

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

Skorzystałem z następującego rozwiązania: kiedy aplikacja ma zamiar zrezygnować, otrzymuję migawkę appWindow jako widok i dodaję do niej rozmycie. Następnie dodaję ten widok do okna aplikacji

jak to zrobić:

  1. w appDelegate tuż przed wdrożeniem dodaj wiersz:

    static const int kNVSBlurViewTag = 198490;//or wherever number you like
  2. dodaj te metody:

    - (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. spraw, aby implementacja appDelegate wyglądała następująco:

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

Stworzyłem Przykładowe projekty w Swift i Objective C . Oba projekty wykonują następujące działania w:

-application: didResignActive - tworzona jest migawka, zamazywana i dodawana do okna aplikacji

-application: willBecomeActive blur view jest usuwany z okna.

Jak używać:

Cel C

  1. Dodaj AppDelegate+NVSBlurAppScreenpliki .h i .m do swojego projektu

  2. w swojej -applicationWillResignActive: metoda dodaj następujący wiersz:

    [self nvs_blurPresentedView];
  3. w swojej metodzie -applicationDidEnterBackground: dodaj następujący wiersz:

    [self nvs_unblurPresentedView];

Szybki

  1. dodaj plik AppDelegateExtention.swift do swojego projektu

  2. w swojej aplikacji funkcja WillResignActive dodaj następujący wiersz:

    blurPresentedView()
  3. w funkcji applicationDidBecomeActive dodaj następujący wiersz:

    unblurPresentedView()
Nikolay Shubenkov
źródło
2

jeśli jest używany tylko [self.window addSubview:imageView];w applicationWillResignActivefunkcji, ten imageView nie obejmie UIAlertView, UIActionSheet ani MFMailComposeViewController ...

Najlepszym rozwiązaniem jest

- (void)applicationWillResignActive:(UIApplication *)application
{
    UIWindow *mainWindow = [[[UIApplication sharedApplication] windows] lastObject];
    [mainWindow addSubview:imageView];
}
Zorot
źródło
Bardzo dobre rozwiązanie, jeśli masz wideo odtwarzane w trybie pełnoekranowym w widoku sieciowym!
Tommie
Doskonały!. Działa nawet jeśli mamy otwartą klawiaturę przed wejściem do przełącznika wielozadaniowego! Dzięki
Prabhu.Somasundaram
0

Podaję własne rozwiązanie jako „odpowiedzi”, chociaż to rozwiązanie jest bardzo zawodne. Czasami jako zrzut ekranu pojawia się czarny ekran, czasami XIB, a czasami zrzut ekranu z samej aplikacji. W zależności od urządzenia i / lub czy uruchomię to w symulatorze.

Należy pamiętać, że nie mogę podać żadnego kodu dla tego rozwiązania, ponieważ zawiera wiele szczegółów dotyczących aplikacji. Ale to powinno wyjaśnić podstawową istotę mojego rozwiązania.

W AppDelegate.m pod applicationWillResignActive sprawdzam, czy korzystamy z iOS7, czy ładuję nowy widok, który jest pusty z logo aplikacji pośrodku. Po wywołaniu applicationDidBecomeActive ponownie uruchamiam moje stare widoki, które zostaną zresetowane - ale to działa w przypadku typu aplikacji, którą tworzę.

Tommie
źródło
1
Jeśli widzisz, co robi aplikacja Aparat - zmienia obraz w rozmazany, gdy przechodzisz w tło (możesz zobaczyć przejście, ponieważ zmniejsza się do ikony)
Petesh
@Petesh Tak, to fajna implementacja, ale pytanie brzmi, jak oni to robią. Boję się prywatnego API.
Rob
@Tommie Mówisz „W zależności od urządzenia i / lub czy uruchomię to w symulatorze”. Wydaje mi się, że działa na urządzeniu (chociaż nie przeprowadziłem wyczerpujących testów), ale nie na symulatorze. Czy zauważyłeś, że nie działa na urządzeniu?
Rob
0

Możesz użyć aktywatora, aby skonfigurować podwójne kliknięcie przycisku głównego, aby uruchomić wielozadaniowość i wyłączyć domyślne podwójne kliknięcie przycisku głównego i uruchamianie okna wielozadaniowości. Tej metody można użyć do zmiany zrzutów ekranu na domyślny obraz aplikacji. Dotyczy to aplikacji z domyślną funkcją ochrony hasłem.

Fareed.MK
źródło
-2

Xamarin.iOS

Na podstawie https://stackoverflow.com/a/20040270/7561

Zamiast tylko pokazywać kolor, chciałem pokazać mój ekran startowy.

 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
źródło