Any way to disable "screenshots" on iOS like WhatsApp [duplicate] - ios

I need to prevent screen capture by users of my app, for security reasons. The contents I display are confidential and should not be copied onto the device. I saw one answer on Stack Overflow, but for Android.
Is it possible somehow in iOS to prevent screen capture?
While capturing the screenshot into the gallery by the click of few buttons is a very useful feature for the user, there is a limited requirement to prevent it too. Any pointers?

I've just wrote simple extension of UIView that allows to hide it from screen-capturing, Airplay mirroring and so on. The solution uses ability of UITextField to hide a password from capturing.
extension UIView {
func makeSecure() {
DispatchQueue.main.async {
let field = UITextField()
field.isSecureTextEntry = true
self.addSubview(field)
field.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
field.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
self.layer.superlayer?.addSublayer(field.layer)
field.layer.sublayers?.first?.addSublayer(self.layer)
}
}
}
using:
class ViewController: UIViewController {
var secureView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
secureView.makeSecure()
}
}
I'll be grateful if someone explains how Apple does this magic inside.

There's no way to prevent taking screenshots entirely. You can do what Snapchat does, which is by requiring the user to be touching the screen to view whatever information you're displaying. This is because the system screenshot event interrupts touches. It's not a perfect method and you can't prevent users from taking screenshots 100% of the time.
More details: iOS Detection of Screenshot?

It's been a while, but I just came across ScreenShieldKit, which is a patent-pending technology used by the messaging app Confide. What it does is that it let's the user take screenshots, but the content is blank on the end picture. They recently released the iOS version.

Remove sensitive information from views before moving to the
background. When an application transitions to the background, the
system takes a snapshot of the application’s main window, which it
then presents briefly when transitioning your application back to the
foreground. Before returning from your applicationDidEnterBackground:
method, you should hide or obscure passwords and other sensitive
personal information that might be captured as part of the snapshot.
In swift 4 add this code to your app delegate.
Declare a variable in app delegate
var imageview : UIImageView?
func applicationWillResignActive(_ application: UIApplication) {
imageview = UIImageView.init(image: UIImage.init(named: "bg_splash"))
self.window?.addSubview(imageview!)
// 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 invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidBecomeActive(_ application: UIApplication) {
if (imageview != nil){
imageview?.removeFromSuperview()
imageview = nil
}
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}

Here's a great Swift package used for hide view from system screenshots and video recording: SnapshotSafeView

I've heard that you can listen for a screenshot event using UIApplicationUserDidTakeScreenshotNotification
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationUserDidTakeScreenshotNotification
object:nil
queue:mainQueue
usingBlock:^(NSNotification *note) {
// executes after screenshot
NSLog(#"Screenshot Detection : %#", note);
UIAlertView *screenshotAlert = [[UIAlertView alloc] initWithTitle:#"Screenshot Detected" message:#"Oh Oh no screenshot bruhh" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[screenshotAlert show];
}];
what if you could immediately delete the screenshot file when it was made?

You can use this extension below. Please apply this method to the parent view
extension UIView {
func preventScreenshot(for view: UIView) {
let textField = UITextField()
textField.isSecureTextEntry = true
textField.isUserInteractionEnabled = false
guard let hiddenView = textField.layer.sublayers?.first?.delegate as? UIView else {
return
}
hiddenView.subviews.forEach { $0.removeFromSuperview() }
hiddenView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(hiddenView)
hiddenView.fillSuperview()
hiddenView.addSubview(view)
}
}
So for instance to be able to prevent screenshots on a scrollView
private weak var scrollView: UIScrollView! (it's an outlet)
in your viewDidLoad, just do this below
self.view.preventScreenshot(for: self.scrollView)
Note: fillSuperview is just anchoring your view to its superview so it's like below:
NSLayoutConstraint.activate([
hiddenView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
hiddenView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
hiddenView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
hiddenView.topAnchor.constraint(equalTo: self.topAnchor)
])

You can this tool. It hides sensitive information from screenshot.
https://screenshieldkit.com

I use the following package to prevent screenshots of my own generated QR code.
https://github.com/shvul/PrivateImage
So far I have been able to use it without any problems.

Related

UISegmentedControl as UINavigationBar title item loads after some delay

Since I updated to Xcode8/iOS10 & Swift 3 I have this weird issue on segmented controls used as navigation bar titles.. it takes some time to appear. Or I can touch the area (invisible at that time) and it will appear.
I believe the item as been loaded but just not be drawn and then it got either draw by touching the area or because of the event loop asking for view redraw.
UI is not blocked.
can't find a way to fix it.
PS: could not reproduce on fresh new project
Breaking with Debug View Hierarchy, it appears the UISegmentedControls lives but is not initialised with colors/texts.. then it will, few seconds later.
Ok this was due to threads.
My app on loading was initialising a core data stack. On the callback of it I was instantiating the right view controller using window.rootViewController = vc, this needs to be executed on the main thread, but the completion of the core data stack init was launch on a background one.
Something weird yet. I had implemented a UIWindow extension
extension UIWindow {
func setRootViewController(with viewController: UIViewController) {
DispatchQueue.main.async {
self.rootViewController = viewController
}
}
}
// beeing in background thread
self.window.setRootViewController(with: self.rootVC)
this will produce the issue below.
but the next will work
extension UIWindow {
func setRootViewController(with viewController: UIViewController) {
self.rootViewController = viewController
}
}
// beeing in background thread
DispatchQueue.main.async {
self.window.setRootViewController(with: self.rootVC)
}
don't know what's the difference.

Run Code Before First View Controller is Initialized (Storyboard-based App)

My app needs to perform some cleanup tasks -delete old data stored locally- every time on startup, before the initial view controller is displayed.
This is because that initial view controller loads the existing data when initialized, and displays it inside a table view.
I set up several breakpoints and found out that the initial view controller's initializer (init?(coder aDecoder: NSCoder)) is run before application(_:didFinishLaunchingWithOptions) - even before application(_:**will**FinishLaunchingWithOptions), to be precise.
I could place the cleanup code at the very top of the view controller's initializer, but I want to decouple the cleanup logic from any particular screen.
That view controller may end up not being the initial view controller one day.
Overriding the app delegate's init() method and putting my cleanup code there does get the job done, but...
Question:
Isn't there a better / more elegant way?
Note: For reference, the execution order of the relevant methods is as
follows:
AppDelegate.init()
ViewController.init()
AppDelegate.application(_:willFinishLaunchingWithOptions:)
AppDelegate.application(_:didFinishLaunchingWithOptions:)
ViewController.viewDidLoad()
Clarification:
The cleanup task is not lengthy and does not need to run asynchronously: on the contrary, I'd prefer if my initial view controller isn't even instantiated until it completes (I am aware that the system will kill my app if it takes to long to launch. However, it's just deleting some local files, no big deal.).
I am asking this question mainly because, before the days of storyboards, app launch code looked like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
// INITIAL VIEW CONTROLLER GETS INSTANTIATED HERE
// (NOT BEFORE):
MyViewController* controller = [[MyViewController alloc] init];
self.window.rootViewController = controller;
[self.window makeKeyAndVisible];
return YES;
}
...but now, because main storyboard loading happens behind the scenes, the view controller is initialized before any of the app delegate methods run.
I am not looking for a solution that requires to add custom code to my initial view controller.
I am not sure if there is more elegant way but there are definitely some other ways...
I'd prefer if my initial view
controller isn't even instantiated until it completes
This is not a problem. All you have to do is to delete a UIMainStoryboardFile or NSMainNibFile key from the Info.plist which tells the UIApplicationMain what UI should be loaded. Subsequently you run your "cleanup logic" in the AppDelegate and once you are done you initiate the UI yourself as you already shown in the example.
Alternative solution would be to create a subclass of UIApplicationMain and run the cleanup in there before the UI is loaded.
Please see App Life Cycle below:
You can add a UIImageView on your initial ViewController which will contain the splash image of your app.
In viewDidLoad()... make the imageview.hidden property False... do you cleanup operation and on completion of cleanup task make the imageview.hidden property TRUE.
By this user will be unaware of what job you are doing and this approach is used in many recognized app.
I faced a very simmilar situation
where i needed to run code, which was only ready after didFinishLaunchingNotification
i came up with this pattern which also works with state restoration
var finishInitToken: Any?
required init?(coder: NSCoder) {
super.init(coder: coder)
finishInitToken = NotificationCenter.default.addObserver(forName: UIApplication.didFinishLaunchingNotification, object: nil, queue: .main) { [weak self] (_) in
self?.finishInitToken = nil
self?.finishInit()
}
}
func finishInit() {
...
}
override func decodeRestorableState(with coder: NSCoder) {
// This is very important, so the finishInit() that is intended for "clean start"
// is not called
if let t = finishInitToken {
NotificationCenter.default.removeObserver(t)
}
finishInitToken = nil
}
alt you can make a notif for willFinishLaunchingWithOptions
Alternatively, if you want a piece of code to be run before everything, you can override the AppDelegate's init() like this:
#main
class AppDelegate: UIResponder, UIApplicationDelegate {
override init() {
DoSomethingBeforeEverthing() // You code goes here
super.init()
}
...
}
Few points to remember:
You might want to check, what's allowed/can be done here, i.e. before app delegate is initialized
Also cleaner way would be, subclass the AppDelegate, and add new delegate method that you call from init, say applicationWillInitialize and if needed applicationDidInitialize

How can I "remove" the embed segue in my container view, or remove the container view completely?

I have a splashViewController with a containerView, which is created in Storyboard.
In my story board, I automatically drag an embed segue from my containerView to a profileViewController.
Inside my splashViewController, I want to programatically "destroy" the containerView + profileViewController(both of them).
I've tried this:
self.containerView.hidden = true //obviously doesn't work. it's just visual
self.containerView.removeFromSuperView() //nope. it's just visual.
How can I remove both containerView and profileViewController completely, making sure that both deinit appropriately? If that's not possible, can I at least deinit the profileViewController? (I'll just set containerView as hidden).
Note: I play a movie video automatically (looping) in my profileViewController. It has sound, and even when I set containerView to nil and remove it from superview, the sound keeps playing.
I wrote up a quick example project here demonstrating the solution here.
An autoplaying video (with dog barking) automatically plays in the bottom of the screen. Pressing the "Stop Player" button removes the child UIViewController and the container.
MPMoviePlayerController finished playing the audio even after the moviePlayer was stopped or set to nil (although not necessarily deallocated if an internal part of the MPMoviePlayerController held a strong reference which I suspect was the problem).
Anyway, I found a workaround by setting the contentURL of the movie player to nil on deinit before calling moviePlayer.stop()
//The `ProfileViewController` is created in the storyboard as a child of `ViewController`.
class ViewController: UIViewController {
#IBOutlet weak var container: UIView?
#IBAction func stopPlayer(sender: UIButton) {
//We remove the child view controller (the ProfileViewController) from the parent
for controller in self.childViewControllers {
if let child = controller as? ProfileViewController {
child.removeFromParentViewController() //This causes the deinit call
container?.removeFromSuperview() //Clear up the container (stays as a black box otherwise)
}
}
}
}
In the ProfileViewController, we have to change the contentURL and stop our movie player
class ProfileViewController: UIViewController {
lazy var moviePlayer: MPMoviePlayerController = {
let path = NSBundle.mainBundle().pathForResource("TestMovie", ofType: "mp4")!
let moviePlayer = MPMoviePlayerController(contentURL: NSURL(fileURLWithPath: path)!)
moviePlayer.movieSourceType = .File
moviePlayer.prepareToPlay()
moviePlayer.view.frame = self.view.frame
return moviePlayer
}()
override func viewDidAppear(animated: Bool) {
view.addSubview(moviePlayer.view)
moviePlayer.play()
//Looping
NSNotificationCenter.defaultCenter().addObserver(self, selector: "moviePlayerDidFinish:", name:MPMoviePlayerPlaybackDidFinishNotification, object: moviePlayer)
}
//Change our contentURL then stop
deinit {
moviePlayer.contentURL = nil
moviePlayer.stop()
}
#objc func moviePlayerDidFinish(notification: NSNotification) {
let reasonInt = notification.userInfo![MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] as! Int
let reason = MPMovieFinishReason(rawValue: reasonInt)
if (reason == MPMovieFinishReason.PlaybackEnded) {
moviePlayer.play()
}
}
}
Tested on a iPhone 5 device and on the simulator
tl;dr
Remove the container UIView itself by calling removeFromSuperview
The MPMoviePlayerController keeps playing audio after being stopped, this is a bug. Workaround it by setting the contentURL then stopping
There are some details missing here, so I'll try to do my best, but it sounds like containerView is not the main view of that UIViewController, as I don't think you can say self.view.removeFromSuperView()
If you want to have a pattern where you delete the container view, I would recommend taking a look at how Apple suggests that you do this with their ContainerViewController There are a few more steps involved but it allows you to easily add and remove children. The downside is that you need to have a ViewController associated with each view.
The issue that it sounds like you are dealing with is this MPMoviePlayerController. MPMoviePlayerController requires a view associated with it. From Apple's documentation
Playback occurs in a view owned by the movie player and takes place
either fullscreen or inline. You can incorporate a movie player’s view
into a view hierarchy owned by your app, or use an
MPMoviePlayerViewController object to manage the presentation for you.
My guess would be without looking at any code, that MPMoviePlayerController is hanging onto a reference to one of the view's that your deleting and its a strong reference so it isn't letting you actually delete that view and causing it to continue without being on screen.
It looks like MPMoviePlayerController is deprecated in ios9 so you should move to AVPlayer, which should give you more control over a movie inside your view
I think you should find a way to stop playing sounds. Also, you can just assign nil to those view controllers you want to deallocate. However, you can not do self = nil;.

How to detect that speech recogntion is in progress

Problem:
I have UITextField side by side with UIButton with send functionality. When user presses send button I'm performing simple action:
- (IBAction)sendMessage: (id)sender {
[self.chatService sendMessage: self.messageTextField.text];
self.messageTextField.text = #""; // here I get exception
}
Now when user starts using dictation from keyboard, then presses done on dictation view (keyboard) and immediately presses send button, I've got exception "Range or index out of bounds".
Possible solution:
I've noticed that other applications disable this "send" button when speech recognition server is processing data. This is exactly between two events: user presses "done" and results are appearing in text field. I wish to solve it in the same manner.
I've problem finding in documentation where this notification can be received. I've found UITextInput protocol, but this is not what I need.
Similar topics:
Using Dictation - iOS 6 - DidStart - solution not acceptable (might be rejected by apple)
Disable Dictation button on the keyboard of iPhone 4S / new iPad - similar approach as above
What have I tried:
simply catch and ignore exception. Crash didn't acured, but virtual keyboard become completely unresponsive
Disabling send button when [UITextInputMode currentInputMode].primaryLanguage is equal #"dictation". Notification UITextInputCurrentInputModeDidChangeNotification which reports end of dictation mode arrives before dictation service commits new value and I'm still able to click send button to cause exception. I could add delay when primaryLanguage losses #"dictation" value, but I don't like this approach. Most probably this required delay depends how much speech recognition service is responsive.
I've added bunch of actions on different events (this evets was looking processing: UIControlEventEditingDidBegin, UIControlEventEditingChanged, UIControlEventEditingDidEnd, UIControlEventEditingDidEndOnExit). The good thing is that it looks like UIControlEventEditingChanged is fired exactly at desired moments: when user presses "Done" on dictation view and when service is committing or ending dictation. So this is my best concept so far. The bad thing is that this is fired in other cases too and there is no information to distinguish in which case this control event was fired, so I don't know should I disable or enable the button or do nothing.
I finally found ultimate solution.
It is simple elegant will pass apple review and it Always work. Just react on UIControlEventEditingChanged and detect existance of replacemnt characterlike this:
-(void)viewDidLoad {
[super viewDidLoad];
[self.textField addTarget: self
action: #selector(eventEditingChanged:)
forControlEvents: UIControlEventEditingChanged];
}
-(IBAction)eventEditingChanged:(UITextField *)sender {
NSRange range = [sender.text rangeOfString: #"\uFFFC"];
self.sendButton.enabled = range.location==NSNotFound;
}
Old approach
Finlay I've found some solution. This is improved concept nr 3 with mix of concept nr 2 (based on that answer).
-(void)viewDidLoad {
[super viewDidLoad];
[self.textField addTarget: self
action: #selector(eventEditingChanged:)
forControlEvents: UIControlEventEditingChanged];
}
-(IBAction)eventEditingChanged:(UITextField *)sender {
NSString *primaryLanguage = [UITextInputMode currentInputMode].primaryLanguage;
if ([primaryLanguage isEqualToString: #"dictation"]) {
self.sendButton.enabled = NO;
} else {
// restore normal text field state
self.sendButton.enabled = self.textField.text.length>0;
}
}
- (IBAction)sendMessage: (id)sender {
[self.chatService sendMessage: self.messageTextField.text];
self.messageTextField.text = #"";
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if (self.textField.text.length==0 || !self.sendButton.enabled) {
return NO;
}
[self sendMessage: textField];
return YES;
}
// other UITextFieldDelegate methods ...
Now problem doesn't appears since user is blocked when it could happen (exactly between user presses "Done" button on dictation view and when results are coming from speech recognition service.
The good thing is that public API is used (only #"dictation" can be a problem, but I thin it should be accepted by Apple).
In iOS 7 Apple introduced TextKit so there are new information for this question:
NSAttachmentCharacter = 0xfffc
Used to denote an attachment as documentation says.
So, if your version is more or equal to 7.0, better approach is to check attributedString for attachments.

Controlling the screenshot in the iOS 7 multitasking switcher

I've been trying to find some information regarding the new multitasking switcher in iOS 7 and especially the screenshot that the OS takes when the app is going into hibernation.
Is there any way to completely turn off this feature or screenshot? Or can I hide the app altogether from the switcher? The app needs to run in the background, but we do not want to show any screenshot from the app.
The screenshot is potentially a security-risk, think along the lines for banking-apps where your card number or account summary will be available to anyone that double-click on the home button on the device.
Anyone with any insight into this? Thanks.
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];
}
}
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:
in appDelegate just before implementation add line:
static const int kNVSBlurViewTag = 198490;//or wherever number you like
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;
}
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
Add AppDelegate+NVSBlurAppScreen .h and .m files to your project
in your -applicationWillResignActive: method add the following line:
[self nvs_blurPresentedView];
in your -applicationDidEnterBackground: method add the following line:
[self nvs_unblurPresentedView];
Swift
add AppDelegateExtention.swift file to your project
in your applicationWillResignActive function add the following line:
blurPresentedView()
in your applicationDidBecomeActive function add the following line:
unblurPresentedView()
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];
}
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.
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.
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();
}

Resources