控制 iOS7多任务切换器中的屏幕截图

我一直试图找到一些关于 iOS7中新的多任务切换器的信息,特别是当应用程序进入休眠状态时系统截图。

enter image description here

有没有办法完全关闭这个功能或屏幕截图?或者我可以把这个应用程序完全隐藏起来吗?该应用程序需要在后台运行,但我们不希望显示任何截图从应用程序。

屏幕截图有潜在的安全风险,考虑一下银行应用程序,在这些应用程序中,任何人只要双击设备上的主页按钮,就可以获得你的卡号或账户摘要。

有人对此有什么见解吗,谢谢。

48024 次浏览

Providing my own solution as an "answers", though this solution is very unreliable. Sometimes i get a black screen as the screenshot, sometimes the XIB and sometimes a screenshot from the app itself. Depending on device and/or if i run this in the simulator.

Please note i cannot provide any code for this solution since it's a lot of app-specific details in there. But this should explain the basic gist of my solution.

In AppDelegate.m under applicationWillResignActive i check if we're running iOS7, if we do i load a new view which is empty with the app-logo in the middle. Once applicationDidBecomeActive is called i re-launch my old views, which will be reset - but that works for the type of application i'm developing.

In Preparing Your UI to Run in the Background, Apple says:

Prepare Your UI for the App Snapshot

At some point after your app enters the background and your delegate method returns, UIKit takes a snapshot of your app’s current user interface. The system displays the resulting image in the app switcher. It also displays the image temporarily when bringing your app back to the foreground.

Your app’s UI must not contain any sensitive user information, such as passwords or credit card numbers. If your interface contains such information, remove it from your views when entering the background. Also, dismiss alerts, temporary interfaces, and system view controllers that obscure your app’s content. The snapshot represents your app’s interface and should be recognizable to users. When your app returns to the foreground, you can restore data and views as appropriate.

See Technical Q&A QA1838: Preventing Sensitive Information From Appearing In The Task Switcher

In addition to obscuring/replacing sensitive information, you might also want to tell iOS 7 to not take the screen snapshot via ignoreSnapshotOnNextApplicationLaunch, whose documentation says:

If you feel that the snapshot cannot correctly reflect your app’s user interface when your app is relaunched, you can call ignoreSnapshotOnNextApplicationLaunch to prevent that snapshot image from being taken.

Having said that, it appears that the screen snapshot is still taken and I have therefore filed a bug report. But you should test further and see if using this setting helps.

If this was an enterprise app, you might also want to look into the appropriate setting of allowScreenShot outlined in the Restrictions Payload section of the Configuration Profile Reference.


Here is an implementation that achieves what I needed. You can present your own UIImageView, or your can use a delegate-protocol pattern to obscure the confidential information:

//  SecureDelegate.h


#import <Foundation/Foundation.h>


@protocol SecureDelegate <NSObject>


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


@end

I then gave my app delegate a property for that:

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

My view controller sets it:

- (void)viewDidLoad
{
[super viewDidLoad];


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

The view controller obviously implements that protocol:

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


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

And, finally, my app delegate avails itself of this protocol and property:

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

Note, I'm using applicationWillResignActive rather than the advised applicationDidEnterBackground, because, as others have pointed out, the latter is not called when double tapping on the home button while the app is running.

I wish I could use notifications to handle all of this, rather than the delegate-protocol pattern, but in my limited testing, the notifications aren't handled in a timely-enough manner, but the above pattern works fine.

This is the solution I worked with for my application:

As Tommy said: You can use the applicationWillResignActive. What I did was making a UIImageView with my SplashImage and add it as subview to my main window-

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

I used this method instead of applicationDidEnterBackground because applicationDidEnterBackground won't be triggered if you doubletap the home button, and applicationWillResignActive will be. I heard people say though it can be triggered in other cases aswell, so I'm still testing around to see if it gives problem, but none so far! ;)

Here to remove the imageview:

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

Hope this helps!

Sidenote: I tested this on both the simulator and a real device: It Won't Show on the simulator, but it does on a real device!

This quick and easy method will yield a black snapshot above your app's icon in the iOS7 or later app switcher.

First, take your app's key window (typically setup in AppDelegate.m in application:didFinishLaunchingWithOptions), and hide it when your app is about to move into the background:

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

Then, un-hide your app's key window when your app becomes active again:

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

At this point, check out the app switcher and verify that you see a black snapshot above your app's icon. I've noticed that if you launch the app switcher immediately after moving your app into the background, there can be a delay of ~5 seconds where you'll see a snapshot of your app (the one you want to hide!), after which it transitions to an all-black snapshot. I'm not sure what's up with the delay; if anyone has any suggestions, please chime in.

If you want a color other than black in the switcher, you could do something like this by adding a subview with any background color you'd like:

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

Then, remove this color subview when your app becomes active again:

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

You can use activator to configure double clicking of home button to launch multitasking and disable default double clicking of home button and launching of multitasking window. This method can be used to change the screenshots to the application's default image. This is applicable to apps with default passcode protection feature.

if only use [self.window addSubview:imageView]; in applicationWillResignActive function, This imageView won't cover UIAlertView, UIActionSheet or MFMailComposeViewController...

Best solution is

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

I used the following solution: when application is going to resign I get appWindow snapshot as a View and add blur to it. Then I add this view to app window

how to do this:

  1. in appDelegate just before implementation add line:

    static const int kNVSBlurViewTag = 198490;//or wherever number you like
    
  2. add this methods:

    - (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. make your appDelegate implementation be the as follows:

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

I created Example projects in Swift and Objective C. Both projects makes the following actions in:

-application:didResignActive - snapshot is created, blurred and added to app window

-application:willBecomeActive blur view is being removed from window.

How to use:

Objecitve C

  1. Add AppDelegate+NVSBlurAppScreen .h and .m files to your project

  2. in your -applicationWillResignActive: method add the following line:

    [self nvs_blurPresentedView];
    
  3. in your -applicationDidEnterBackground: method add the following line:

    [self nvs_unblurPresentedView];
    

Swift

  1. add AppDelegateExtention.swift file to your project

  2. in your applicationWillResignActive function add the following line:

    blurPresentedView()
    
  3. in your applicationDidBecomeActive function add the following line:

    unblurPresentedView()
    

Xamarin.iOS

Adapted from https://stackoverflow.com/a/20040270/7561

Instead of just showing a color I wanted to show my launch screen.

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