Eden.F
Eden.F

Reputation: 219

Modal view controller does not cover status bar

I'm developing an ios app. I have a a main view and in this view im trying to present a modal view controller with dimmed background(black with opacity). The problem is that the status bar is not affected by this color and remains the same.

This is how i present the view controller:

let shareViewController = self.storyboard?.instantiateViewControllerWithIdentifier("ShareViewController") as! ShareViewController
            shareViewController.battle = battle
            shareViewController.delegate = self
            let animation = CATransition()
            animation.duration = 1
            animation.type = kCATransitionFade
            self.view.window?.layer.addAnimation(animation, forKey: kCATransition)
            presentViewController(shareViewController, animated: false) {
               () in 
               // nothing here
            }

Here are some screenshots to demonstrate the problem:

This is the problem(status bar color): Problem illustration This is the modal view in storyboard: storyboard

Upvotes: 15

Views: 8182

Answers (7)

Alex
Alex

Reputation: 567

Set your view controller as the root view controller of a UIWindow, then present the window at the UIWindowLevelAlert level.

Below is a Swift 3 class used to animate a modal popup over all other UI elements, including the status bar. A scrim view is used to shade background UI and intercept touches to dismiss the view.

import UIKit

class ModalViewController: UIViewController {

    private let scrimView: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = UIColor.black
        view.alpha = 0.0
        return view
    }()

    private var myWindow: UIWindow?

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor.clear

        // Setup scrim View
        view.addSubview(scrimView)
        view.topAnchor.constraint(equalTo: scrimView.topAnchor).isActive = true
        view.leftAnchor.constraint(equalTo: scrimView.leftAnchor).isActive = true
        view.rightAnchor.constraint(equalTo: scrimView.rightAnchor).isActive = true
        view.bottomAnchor.constraint(equalTo: scrimView.bottomAnchor).isActive = true

        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismiss as (Void) -> Void))
        scrimView.addGestureRecognizer(tapGestureRecognizer)

        // Layout custom popups or action sheets
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        UIView.animate(withDuration: 0.25) {
            self.scrimView.alpha = 0.5
            // Animate in custom popups or action sheets
        }
    }

    func present() {
        myWindow = UIWindow(frame: UIScreen.main.bounds)
        myWindow?.windowLevel = UIWindowLevelAlert
        myWindow?.backgroundColor = UIColor.clear
        myWindow?.rootViewController = self
        myWindow?.isHidden = false
    }

    func dismiss() {

        UIView.animate(
            withDuration: 0.25,
            animations: {
                self.scrimView.alpha = 0.0
                // Animate out custom popups or action sheets
            },
            completion: { success in
                self.myWindow = nil
            }
        )
    }
}

To present the view:

let modalView = ModalViewController()
modalView.present()

To dismiss the view, tap anywhere on the scrim.

Upvotes: 3

Ali ZahediGol
Ali ZahediGol

Reputation: 1096

you can add this code to view controller for Swift 3:

let statusView: UIView = UIView(frame: CGRect(x: 0.0, y: -20.0, width: UIScreen.main.bounds.size.width, height: 20.0))
statusView.backgroundColor = UIColor.black
statusView.alpha = 0.8
self.addSubview(self.statusView)

Upvotes: 0

Sulthan
Sulthan

Reputation: 130082

I cannot reproduce your problem, the following code works without problems in my single view app:

let viewController = UIViewController()
viewController.modalPresentationStyle = .overFullScreen
viewController.view.backgroundColor = UIColor.black.withAlphaComponent(0.5)

let animation = CATransition()
animation.duration = 1
animation.type = kCATransitionFade
self.view.window?.layer.add(animation, forKey: kCATransition)

self.present(viewController, animated: false, completion: nil)

However note that you should be presenting over the root controller of the view. Sometimes you can get strange effects when presenting from your internal controllers:

self.view.window?.rootViewController?.present(viewController, animated: false, completion: nil)

Also make sure you are using the correct modalPresentationStyle.

Example

Upvotes: 13

Giuseppe Lanza
Giuseppe Lanza

Reputation: 3699

Custom animation transitions should be performed using UIViewControllerAnimatedTransitioning. Here is a tutorial for this purpose: https://www.raywenderlich.com/110536/custom-uiviewcontroller-transitions

If all you want is a fade animation you can have it by changing the modalTransitionStyle property of the viewController you are going to display.

Try by fixing your code this way:

guard let shareViewController = self.storyboard?.instantiateViewControllerWithIdentifier("ShareViewController") as! ShareViewController else { 
    //Fallback in case of nil?
    return 
}
shareViewController.modalTransitionStyle = .crossDissolve
presentViewController(shareViewController, animated: true, completion: nil)

Also please note that presentViewController(shareViewController, animated: true, completion: nil) is for swift 2. The equivalent swift 3 would be present(shareViewController, animated: true, completion: nil)

Upvotes: 0

Fahri Azimov
Fahri Azimov

Reputation: 11770

Here is the solution you might be looking for:

if let window = UIApplication.shared.keyWindow {
   window.windowLevel = UIWindowLevelStatusBar + 1
}

The main idea behind this code is, window of your application has a window level which is lower than status bar window level. And what this code does is, just put your window's window level higher than status bar window level, and your window can now cover the status bar. Don't forget, this code has to be called on main thread, just before presenting your view controller. Good luck!

Upvotes: 0

ClayJ
ClayJ

Reputation: 432

You could be extremely practical and simply hide the status bar when your modal view controller is up:

    override func prefersStatusBarHidden() -> Bool {
    return true
}

Upvotes: -2

JuicyFruit
JuicyFruit

Reputation: 2668

this code works for me, when I am presenting UIViewController with alpha != 1. present UIViewController like:

let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let destinationVC = storyBoard.instantiateViewController(withIdentifier: "AddComment") as! AddCommentViewController
destinationVC.modalPresentationStyle = .overCurrentContext //this line is important
destinationVC.delegate = self
destinationVC.restId = self.restaurant.id
self.present(destinationVC, animated: true, completion: nil)

then in destinationVC view controller

override func viewWillDisappear(_: Bool) {
        UIView.animate(withDuration: 1, animations: { () in
            self.view.backgroundColor = .clear
        })
        super.viewWillDisappear(true)
    }

override func viewWillAppear(_: Bool) {
    UIView.animate(withDuration: 1, animations: { () in
        self.view.backgroundColor = UIColor.black.withAlphaComponent(0.5)
    })
    super.viewWillAppear(true)
}

and set its backgroundColor to .clear in viewDidLoad or storyboard. So UIViewController covers whole screen including status bar.

Upvotes: 0

Related Questions