1
votes

I'm trying to display a blurred background over a ViewController that contains a UITableView and is displayed modally. But I can't seem to get the blur effect to cover the entire screen, specifically the Navigation and Status Bars. Below is a screenshot of the blur effect covering the area below the Navigation Bar but not above it -- this happens when I set the frame of the UIVisualEffectView to view.bounds. (Note: The blur effect is designed to be displayed at the same time as the title screen w/ keyboard, and the title screen has a clear + non-opaque background to accommodate this blur effect.)

enter image description here

Interestingly, when I set the frame of the UIVisualEffectView to view.frame (rather than view.bounds), the blur effect only covers about 2/3rds of the area that view.bound covers. Not sure why it's doing this.

Below is what I have in my code. As you can see, when the 'Done' button is pressed, the app generates the ActionViewController (the title screen) along with the blurred background which is called through a delegate method.

@IBAction func donePressed(_ sender: UIButton) {
    let vc = ActionViewController()
    self.definesPresentationContext = true
    self.providesPresentationContextTransitionStyle = true
    vc.modalPresentationStyle = .overFullScreen
    vc.modalTransitionStyle = .coverVertical
    
    self.present(vc, animated: true, completion: nil)
    self.overlayBlurredBackgroundView()
    vc.delegate = self
}

extension PreviewViewController: ActionViewControllerDelegate {
    func overlayBlurredBackgroundView() {
        let blurredBackgroundView = UIVisualEffectView()
        blurredBackgroundView.frame = view.bounds
        blurredBackgroundView.effect = UIBlurEffect(style: .systemThinMaterialDark)
        view.addSubview(blurredBackgroundView)
}
2

2 Answers

1
votes

You need to put the blur overlay view in the presented view controller instead of presenting view controller i.e. ActionViewController. As far as frame is concern, just add right constraint and it will be layout automatically (no need to set frame) for example if you want your blur view to cover the entire screen you can add these constraints.

In the viewDidLoad function of ActionViewController call this function

func addOverlayBlurredBackgroundView() {
    let blurView = UIVisualEffectView()
    blurView.effect = UIBlurEffect(style: .systemThinMaterialDark)
    self.view.insertSubview(blurView, at: 0)

    blurView.translatesAutoresizingMaskIntoConstraints = false

    blurView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0.0).isActive = true
    blurView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0.0).isActive = true
    blurView.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 0.0).isActive = true
    blurView.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: 0.0).isActive = true
}
1
votes

First off, what Muhammad suggested should work. The reason your code crashes could be you are attempting to add the constraints first before adding the blurView to your view as a subview. The key phrase is:

they have no common ancestor.

Don't do that. Always add your subview before constraining it.


Lastly, one easy way to achieve what you want to achieve is to just toggle your navigationBar's visibility whenever you present your transparent screen (the one with the keyboard) and then put the navigationBar back to visible when you're done. Like so:

func overlayBlurredBackgroundView() {
  self.navigationController?.navigationBar.isHidden = true
  
    let blurredBackgroundView = UIVisualEffectView()
    //blurredBackgroundView.frame = view.bounds
    blurredBackgroundView.effect = UIBlurEffect(style: .systemThinMaterialDark)
    view.addSubview(blurredBackgroundView)
  
    blurredBackgroundView.translatesAutoresizingMaskIntoConstraints = false
    blurredBackgroundView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0.0).isActive = true
    blurredBackgroundView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0.0).isActive = true
    blurredBackgroundView.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 0.0).isActive = true
    blurredBackgroundView.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: 0.0).isActive = true

}

and then putting it back when you're removing it:

func removeBlurredBackgroundView() {
  self.navigationController?.navigationBar.isHidden = false
    for subview in view.subviews {
        if subview.isKind(of: UIVisualEffectView.self) {
            subview.removeFromSuperview()
        }
    }
}