Teja Nandamuri
Teja Nandamuri

Reputation: 11201

How to hide the iOS app content view when app is not active?

I'm trying to add a cover screen to app when app is pushed to background by user.

I have these two methods on AppDelegate to add/remove the cover view.

func showCover(){
  let blackV = UIView(frame: UIApplication.shared.keyWindow?.bounds ?? .zero)
  blackV.backgroundColor = .black
  blackV.tag = kCoverTag
  let imgV = UIImageView(image: UIImage(named: "Logo-White"))
  imgV.center = blackV.center
  blackV.addSubview(imgV)
  UIApplication.shared.keyWindow?.addSubview(blackV)
}

func hideCover(){
  if let blackV = UIApplication.shared.keyWindow?.subviews.filter({$0.tag == kCoverTag}).first{
     blackV.removeFromSuperview()
   }
}

The tag value is declared on AppDelegate:

 let kCoverTag = 987

In the applicationWillResignActive , I apply the cover

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

and in didBecomeActive, I remove the cover.

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

This works fine when app is sent to background by pressing home button.

Right after the applicationDidBecomeActive is called, when I print the subviews of keyWindow , I get the output as:

po UIApplication.shared.keyWindow?.subviews
▿ Optional<Array<UIView>>
  ▿ some : 2 elements
    - 0 : <UITransitionView: 0x7f97bd82ffc0; frame = (0 0; 414 896); autoresize = W+H; layer = <CALayer: 0x6000028ecce0>>
    - 1 : <UIView: 0x7f97bae27f20; frame = (0 0; 414 896); tag = 987; layer = <CALayer: 0x600002868400>>

This is good as it shows the cover view as one of the subview added to the key window.

Now, when the app is sent to background by calling UIApplication.shared.openURL , the applicationWillResignActive is called as expected.

And when the user press the "Back to app button" , the app will be shown back to user.

Now,when I print the subviews of key window in the didBecomeActive method right before calling hideCover() method, it looks like it has only one subview.

po UIApplication.shared.keyWindow?.subviews
▿ Optional<Array<UIView>>
  ▿ some : 1 element
    - 0 : <UITransitionView: 0x7f97bfc05510; frame = (0 0; 414 896); autoresize = W+H; layer = <CALayer: 0x6000029984c0>>

Since this shows only one view, the hideCover() method couldn't find the subview and hence couldn't remove it. But in the app I could see the cover view .

I'm wondering why the keyWindow subviews didn't show the added cover view.

I'm testing this in simulator.

Update:

I did two separate print calls

Right after adding the cover view:

print("resign active",UIApplication.shared.keyWindow?.subviews)

This show two subviews for keyWindow

and right before removing the cover view in didBecomeActive method:

print("did become active",UIApplication.shared.keyWindow?.subviews)

This show only ONE subview for keyWindow

Upvotes: 4

Views: 3472

Answers (2)

thaozz
thaozz

Reputation: 7

I use this to cover my app


  var secureView: UIView?
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)
          secureView = UIView()
                secureView?.backgroundColor = UIColor.white
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }

  public func applicationDidBecomeActive(_ application: UIApplication) {
    self.secureView?.removeFromSuperview()
  }

  public func applicationWillResignActive(_ application: UIApplication) {
    if let window = UIApplication.shared.windows.first, let view = secureView {
      view.frame = window.bounds
      window.addSubview(view)
    }
  }

for someone want to add story board:

override
    func applicationWillResignActive(_ application: UIApplication) {
        showSplashScreen()
    }

    override
    func applicationDidBecomeActive(_ application: UIApplication) {
        removeSplashScreen()
    }
    
    func showSplashScreen() {
        let launchScreen = UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController()
        guard let launchScreen = launchScreen,
              let launchView = launchScreen.view else {
            return
        }
        
        launchView.tag = 8888
        launchView.frame = window.bounds
        self.window?.addSubview(launchView)
        self.window?.makeKeyAndVisible()
    }
    
    func removeSplashScreen() {
        guard let window = UIApplication.shared.windows.last,
              let view = window.viewWithTag(8888) else {
            return
        }
        view.removeFromSuperview()
    }

Upvotes: 1

Rob Napier
Rob Napier

Reputation: 299335

Don't use keyWindow here. That's not the right tool. There can be other windows created by the system and may have input focus via makeKey(). Present the view from the window.rootViewController from your UIApplicationDelegate. See QA1838: Preventing Sensitive Information From Appearing In The Task Switcher for Apple's recommended way to implement what you're trying to do. Also see Preparing Your UI to Run in the Background for additional guidance.

Upvotes: 6

Related Questions