1

When an app goes into the background, I want to put a blur view in a xib file (so it can be customized) on top of my top view controller so that users can't see the content of my app when using multitasking. How would I do that? I tried a bunch of stuff with the root view controller but it didn't work. I know I need to use these delegate methods but I am not sure exactly how to get the top view controller and put a view on top of it. I am using tab bar controller with a bunch of navigation controllers.

func applicationWillResignActive(_ application: UIApplication) {
}

and

func applicationWillEnterForeground(_ application: UIApplication) {
}

When the app is opened again, I obviously want to get rid of that blur view but I would also like to use Face/Touch ID to give access. I assume this is pretty straight forward using LocalAuthentication.

Nevin Jethmalani
  • 2,726
  • 4
  • 23
  • 58
  • One way to do that is creating a XIB that will cover entire screen, you can design it as you want with blur, images etc, then you instantiate and call it in your window to cover as a modal – GIJOW Apr 03 '18 at 16:42
  • yes exactly but how would I call it and put it over the current vc in the app delegate? I know how to set it up and design it and all. – Nevin Jethmalani Apr 03 '18 at 16:42
  • @NevinJethmalani Detect iOS app entering background https://stackoverflow.com/questions/34744783/detect-ios-app-entering-background/34745677#34745677 – Leo Dabus Apr 03 '18 at 16:52

4 Answers4

9

In AppDelegate.swift class, please try this code snippet,

let visualEffectView = UIVisualEffectView()

func applicationWillEnterForeground(_ application: UIApplication) {
    self.visualEffectView.removeFromSuperview()
}

func applicationWillResignActive(_ application: UIApplication) {
if !self.visualEffectView.isDescendant(of: self.window!) {
    let blurEffect = UIBlurEffect(style: .light)
    self.visualEffectView = UIVisualEffectView(effect: blurEffect)
    self.visualEffectView.frame = (self.window?.bounds)!
    self.window?.addSubview(self.visualEffectView)
   }
}
func applicationDidBecomeActive(_ application: UIApplication) {
    self.visualEffectView.removeFromSuperview()
}

And yes you are correct about Face ID/Touch ID using LocalAuthentication, you may want to use UIApplicationWillEnterForegroundNotification to detect if the app came to the foreground and show the authentication alert accordingly. I think you need to add this behavior to your ViewController.

Bappaditya
  • 9,494
  • 3
  • 20
  • 29
  • if I wanted to use a xib file how would I do that instead? – Nevin Jethmalani Apr 03 '18 at 16:50
  • It's better to add on top of the window, rather than on top View Controller as you need to add this behavior on all the screens. Anyways if you need to use xib, then please create a xib file and on top of the view add this `UIVisualEffectView` and add this behavior on `BaseViewController` – Bappaditya Apr 03 '18 at 16:53
  • How do I add a custom view from a xib file on top of the window? – Nevin Jethmalani Apr 03 '18 at 16:54
  • 1
    Once added then use the following code snippet to get the added custom view `let customView = UINib(nibName: "nib file name", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as UIView` and add this on window – Bappaditya Apr 03 '18 at 17:02
  • 1
    This works great! Thanks! Now the only problem I have is that it is very abrupt when it shows up. Is there anyway to animate it? I tried using this but it does not work`UIView.animate(withDuration: 1.0) {` – Nevin Jethmalani Apr 03 '18 at 17:43
  • Is there no way to do the local authentication from the app delegate? – Nevin Jethmalani Apr 03 '18 at 17:44
  • Also I got the animation piece. I just had to adjust the alpha. – Nevin Jethmalani Apr 03 '18 at 17:50
  • You may try different `UIBlurEffect` styles for alpha. – Bappaditya Apr 03 '18 at 17:58
2

Rather than adding and removing it from the superview every time, you could leave it and change the effect as needed. Add the following to your AppDelegate:

let visualEffectView = UIVisualEffectView(effect: nil)

func applicationWillResignActive(_ application: UIApplication) {
    if !self.visualEffectView.isDescendant(of: self.window!) {
         self.window?.addSubview(self.visualEffectView)
    }
    self.visualEffectView.frame = (self.window?.bounds)!

    UIView.animate(withDuration: 0.5) {
        self.visualEffectView.effect = UIBlurEffect(style: .light)
    }

}

func applicationWillEnterForeground(_ application: UIApplication) {
    UIView.animate(withDuration: 0.5) {
        self.visualEffectView.effect = nil
    }
}

func applicationDidBecomeActive(_ application: UIApplication) {
    UIView.animate(withDuration: 0.5) {
        self.visualEffectView.effect = nil
    }    
}

This would have the added benefit of you being able to fade the effect in and out.

wottle
  • 13,095
  • 4
  • 27
  • 68
0

To call a view controller from appDelegate you can use this method

func applicationWillResignActive(_ application: UIApplication) {
    self.window = UIWindow(frame: UIScreen.main.bounds)
    let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
    let vcMainView: YourVC = mainStoryboard.instantiateViewController(withIdentifier: "YourVC") as! YourVC
    self.window?.rootViewController?.present(vcMainView, animated: true, completion: nil)
}

Don't forget to set your VC identifier in your Identity Inspector - Identity section

GIJOW
  • 2,307
  • 1
  • 17
  • 37
  • Your code will replace the rootViewController instead of adding a blur over existing one... – Shebuka Apr 03 '18 at 16:54
  • I gave you the path... find your way if it doesn't fit your needs – GIJOW Apr 03 '18 at 16:55
  • I'm not the OP, but your code is not the answer to the question OP asked. Your answer is from the same category of "go google it"... – Shebuka Apr 03 '18 at 16:58
  • really ?? Just think a little and add one line, just one line and get your result. I'm trying to help and SO is taken of people trying to outsource their dev without thinking. Here is your line: `self.window?.rootViewController?.present(vcMainView, animated: true, completion: nil)` – GIJOW Apr 03 '18 at 17:01
0

you can do this to add animation and must have to remove fromsuperview to access other view.if you make self.visualEffectsView = nil it will not allow to access otherview.

func applicationDidBecomeActive(_ application: UIApplication) {
UIView.animate(withDuration: 0.3, animations: {
  self.visualEffectsView.alpha = 0.0
}) { _ in
  self.visualEffectsView.removeFromSuperview()
}
}

func applicationWillResignActive(_ application: UIApplication) {
window?.rootViewController?.view.addSubview(visualEffectsView)
UIView.animate(withDuration: 0.3, animations: {
  self.visualEffectsView.alpha = 1.0
})
}