防止 iOS 在进入后台之前拍摄应用程序的屏幕截图

38

大家可能都知道,iOS在将应用程序置于后台之前会截取应用程序的屏幕截图。这通常是为了提供更好的用户体验,例如快速动画来恢复应用程序等。我不希望我的应用程序屏幕截图被存储在设备上,但是我仍然想要多任务处理继续存在。

我想出了一个解决方案,但我不确定我是否朝着正确的方向发展。因此,当调用 applicationDidEnterBackground 时,我会放置一个覆盖层图像,操作系统会捕获它,并且一旦应用程序进入前台,我就会将覆盖层移除。我不确定这是否有效,但我正在实施它。同时,如果您对此有任何其他想法,将有助于我找到攻击此问题的最佳方法。


你为什么不希望iOS这样做呢?正如你所说,截图是为了执行快速动画... - Srikar Appalaraju
1
我有用户的机密数据...所以有一些第三方工具,比如iPhone Explorer,可以进入iOS文件系统并提取图像。我不想妥协这些数据...因此我计划实施这种方法。 - Mobilewits
这张由操作系统拍摄的截图将存储在iPhone的哪里?@Mobilewits - simbesi.com
10个回答

37

你正在正确的轨道上。这是苹果官方推荐的做法,在iOS应用程序编程指南中有说明

在进入后台之前从视图中删除敏感信息。 当一个应用程序转换到后台时,系统会对应用程序的主窗口进行快照,并在将应用程序切换回前台时短暂地呈现该快照。在从applicationDidEnterBackground:方法返回之前,您应该隐藏或模糊密码和其他可能作为快照的一部分捕获的敏感个人信息。


1
你知道这些屏幕截图在设备上的临时存储位置吗? - Bazze
2
/Library/Caches/Snapshots/$(BUNDLE_IDENTIFIER)/ 是应用程序沙盒内的路径。 - Julian F. Weinert
1
顺便提一下,您可以使用Xcode的DeviceManager下载您的应用程序沙盒容器。 - Julian F. Weinert
更新链接 - Nikhil Manapure
现在不应该是applicationWillResignActive吗? - superarts.org

16

需要在应用程序生命周期方法中编写代码,在应用程序动画到后台时放置一个 imageView

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

这里是删除imageView的代码:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
    if(imageView != nil) {
        [imageView removeFromSuperview];
        imageView = nil;
    }
}

它正在工作,并经过了适当的测试。


但是图片在iPad上没有对齐。 - swathy krishnan
请注意,当显示警报对话框时,也会调用“applicationWillResignActive”。这可能不是您想要显示此图像视图的时间。 - lostintranslation
1
已在iOS 11上进行了测试...它不时地无法正常工作。 - user1872384

12

我也遇到了同样的问题,经过我的研究后,得出以下答案:

  • 在应用程序进入后台之前设置模糊屏幕覆盖,在应用程序变为活动状态时删除此覆盖层

  • 如果是iOS 7或更高版本,则可以使用函数 ignoreSnapshotOnNextApplicationLaunch

请参阅苹果文档: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/Reference/Reference.html#//apple_ref/occ/instm/UIApplication/ignoreSnapshotOnNextApplicationLaunch

希望这能帮助到有需要的人。


ignoreSnapshotOnNextApplicationLaunch听起来很有趣,但这并不能防止iOS在任何情况下截屏,对吧? - saxos
1
我还没有在iOS 8中尝试过这个函数,但在iOS 7中它并不能防止截屏。 - Lily
我在这里遇到了问题。无论我做什么,都无法阻止应用程序截取错误的窗口。在这个页面上说:“…调用此方法让UIKit知道它应该使用你的应用程序的默认启动图片而不是快照。你必须从代码中调用此方法来保留你应用程序的状态。”(待续) - Chris Marshall
2
现在,最后一行是重点。仅在我的applicationWillResignActive回调中调用它不起作用。但是,我不确定Apple所说的“用于保存应用程序状态的代码”具体指什么。 - Chris Marshall
听起来他们是在指你用于持久化状态的代码,以便在重启后仍然保留状态。 - ThinkDigital

10

AppDelegate 中的工作方法,swift 4.2:

func blurScreen(style: UIBlurEffect.Style = UIBlurEffect.Style.regular) {
    screen = UIScreen.main.snapshotView(afterScreenUpdates: false)
    let blurEffect = UIBlurEffect(style: style)
    let blurBackground = UIVisualEffectView(effect: blurEffect)
    screen?.addSubview(blurBackground)
    blurBackground.frame = (screen?.frame)!
    window?.addSubview(screen!)
}

func removeBlurScreen() {
    screen?.removeFromSuperview()
}

在哪里:

weak var screen : UIView? = nil // property of the AppDelegate

在需要的委托方法中调用这些方法:

func applicationWillResignActive(_ application: UIApplication) {
    blurScreen()
}

func applicationDidBecomeActive(_ application: UIApplication) {
    removeBlurScreen()
}

你的 style: UIBlurEffect.Style = UIBlurEffect.Style.regular 子句中存在错误。Style 不是 UIBlurEffect 的成员类型。 - Alex Zavatone

6

你的方法是正确有效的。可以使用覆盖视图,在需要的时候将其删除。如果你的应用程序展示了一些敏感数据,而你不希望这些数据以图片的形式被缓存到任何地方,那么这种做法是合理的。


3

苹果文档 https://developer.apple.com/library/archive/qa/qa1838/_index.html

注意:您的-applicationDidEnterBackground:实现不应启动任何动画(对于任何animated:参数,请传递NO)。从此方法返回后,立即捕获应用程序窗口的快照。在快照被拍摄之前,动画将无法完成。

转换为Swift4.2的苹果代码,我声明了:

func applicationDidEnterBackground(_ application: UIApplication) {
    // Your application can present a full screen modal view controller to
    // cover its contents when it moves into the background. If your
    // application requires a password unlock when it retuns to the
    // foreground, present your lock screen or authentication view controller here.

    let blankViewController = UIViewController()
    blankViewController.view.backgroundColor = UIColor.black

    // Pass NO for the animated parameter. Any animation will not complete
    // before the snapshot is taken.
    window.rootViewController?.present(blankViewController, animated: false)
}

func applicationWillEnterForeground(_ application: UIApplication) {
    // This should be omitted if your application presented a lock screen
    // in -applicationDidEnterBackground:
    window.rootViewController?.dismiss(animated: false) false
}

它仍在运行,当应用程序在后台时,您能否发送屏幕截图? - OurangZeb Khan
它的工作方式是真正创建了一个子视图等,但是当我添加它时,窗口的大小已经很小,因此在快照中出现了视图中间的矩形,但并未覆盖整个屏幕。最终,我按照文档中的建议推送了一个启动屏幕控制器,并稍后将其删除... - Ivan
顺便提一下,我正在使用启动屏幕图像。 - OurangZeb Khan
您正在寻找的页面不存在。 - OurangZeb Khan
这个方法接近正确,但是当 UIAlertController 被呈现时不会起作用。 使用以下代码: // 进入后台 [self.window addSubview:self.splashView]; // 进入前台 [self.splashView removeFromSuperview];可以解决与 UIAlertController 的兼容问题。 - Marc

1

Depak Kumar的改进: 创建一个属性UIImage *snapShotOfSplash;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[UIApplication sharedApplication] ignoreSnapshotOnNextApplicationLaunch];
snapShotOfSplash =[UIImage imageNamed:@"splash_logo"];
}

- (void)applicationDidEnterBackground:(UIApplication *)application {


    self.overlayView = [[UIImageView alloc]initWithFrame:[self.window frame]];
    self.overlayView.backgroundColor = [UIColor whiteColor];
    [self.overlayView setImage:snapShotOfSplash];
    [self.overlayView setContentMode:UIViewContentModeCenter];
    [self.window addSubview:self.overlayView];
    [self.window bringSubviewToFront:self.overlayView]; }

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

1

Swift 4.0版本。

用于使用自定义图标

首先,在AppDelegate的顶部添加此行

var imageView: UIImageView?

并添加这个:

func applicationDidEnterBackground(_ application: UIApplication) {
    imageView = UIImageView(frame: window!.frame)
    imageView?.image = UIImage(named: "AppIcon")
    window?.addSubview(imageView!)
}

func applicationWillEnterForeground(_ application: UIApplication) {
    if imageView != nil {
        imageView?.removeFromSuperview()
        imageView = nil
    }
}

背景为黑色

func applicationDidEnterBackground(_ application: UIApplication) {
    let blankViewController = UIViewController()
    blankViewController.view.backgroundColor = UIColor.black
    window?.rootViewController?.present(blankViewController, animated: false)
}

func applicationWillEnterForeground(_ application: UIApplication) {
    window?.rootViewController?.dismiss(animated: false)
}

1

在后台进行实现并带有一些动画效果,同时具备反向操作功能。

   - (void)applicationWillResignActive:(UIApplication *)application
{
     //     fill screen with our own colour
        UIView *colourView = [[UIView alloc]initWithFrame:self.window.frame];
        colourView.backgroundColor = [UIColor blackColor];
        colourView.tag = 1111;
        colourView.alpha = 0;
        [self.window addSubview:colourView];
        [self.window bringSubviewToFront:colourView];

        // fade in the view
        [UIView animateWithDuration:0.5 animations:^{
            colourView.alpha = 1;
        }];

}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    // grab a reference to our coloured view
    UIView *colourView = [self.window viewWithTag:1111];
    // fade away colour view from main view
    [UIView animateWithDuration:0.5 animations:^{
        colourView.alpha = 0;
    } completion:^(BOOL finished) {
        // remove when finished fading
        [colourView removeFromSuperview];
    }];

 }

-1
在iOS 7中,您可以使用allowScreenShot完全停用该功能。

请参见:Apple Developer: Configuration Profile Reference

allowScreenShot


布尔值
可选。如果设置为false,用户无法保存显示屏幕截图,并且无法捕获屏幕录制;它还防止教室应用程序观察远程屏幕。默认为true

可用性:在iOS 9.0中更新以包括屏幕录制。


@ParikksitBhisay(我知道这是很旧的内容,但我想提一下,因为我在这里处理垃圾邮件)这是一个简短的、可能不太好的答案,但它是一个答案;它提到了所需的属性。 - anon
1
只有在设备受配置管理时才可以使用此功能——例如在企业环境中,常规的AppStore应用程序无法使用。 - Richard Groves

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接