Ios – How to use UIBlurEffect with modal View Controllers


I have a UIViewController that presents another UIViewController modally. I want the modal view controller to have the blur/transparency that iOS 7 introduced. I tried using the new UIVisualEffect but it seems like that only works with UIViews, and not UIViewControllers?

Here is the code I've written, all the views I've added as subviews are what I want to be in the user interface above the blurred out view below it.

In the presenting view controller, I take a screenshot of the screen that I pass to the modal view controller before applying the blur.

In Presenting View Controller:

- (UIImage *)viewImage {
    CGSize size = CGSizeMake(self.view.frame.size.width,self.view.frame.size.height);
    [self.view drawViewHierarchyInRect:(CGRect){CGPointZero, self.view.frame.size.width, self.view.frame.size.height} afterScreenUpdates:YES];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

    return image;

In Modal View Controller:

- (void)viewDidLoad {
    [super viewDidLoad];

    [self.view addSubview:[[UIImageView alloc] initWithImage:self.backgroundImage]];
    //self.backgroundImage is the image that the method above returns, it's a screenshot of the presenting view controller.
    UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
    UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
    [blurEffectView setFrame:self.view.bounds];
    [self.view addSubview:blurEffectView];

    [self.view bringSubviewToFront:self.cancelButton];
    [self.view bringSubviewToFront:self.titleLabel];
    [self.view bringSubviewToFront:self.tableView];

Best Solution

In Swift 3

I found that if you take are doing a modal transition to a new UIViewController and have it over the context with a clear background so you can see the last VC. Using a UIVisualEffect view set in IB doesn't work properly. What you will get it is the blur being rendered at the end of your presentation of the new view.

In interface builder:

Build a modal segue to another view, make the Kind: Present Modally, Transition: Cross Fade. Then give it an identifier like "showMainMenu".

In the destination VC, set it so the first view is a UIImageView with AspectToFit set, with the side set to 0-0-0-0 to each side for the constraints. Second SubView is a UIBlurView set to 0-0-0-0 to the sides of the VC. Then put your elements on top of the UIBlurView like nice contrasty text.

Create a var for the background to set to the UIImageView of the VC you will be segueing to.

var background: UIImage! = UIImage()

Run the segue now and you'll notice the blur pops in and looks terrible.

To solved this issue, write some code to take a snapshot with this extension code for UIImage

    extension UIImage {

        class func takeScreenshot(view: UIView) -> UIImage? {

            // Create screenshot

            view.layer.render(in: UIGraphicsGetCurrentContext()!)
            let screenshot:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
            print("Taking Screenshot")

            return screenshot


Then in my segue I do the following:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    if segue.identifier == "showMainMenu" {

        let vc = segue.destination as! MainViewController

        // I am using a VERY long scroll view so self.view.window! ensures that you only take a picture of your actual view instead of a view that is longer than your screen size.
        let screenshot: UIImage = UIImage.takeScreenshot(view: self.view.window!)!

        vc.background = screenshot


once segued, in the new view controller make sure you run this.

// set the image over to the background image. 

    override func viewWillAppear(_ animated: Bool) {
        self.backgroundImage.image = background


    override func viewDidAppear(_ animated: Bool) {

        // prevent the blurView for just popping in due to the segue
        UIView.animate(withDuration: 0.3, delay: 0.0, options: .curveEaseInOut, animations: {

            // make the background transparent so you can now see what is beneath that layer.
            self.backgroundImage.alpha = 0

This way you can get the sweet modal IB Fade in without too much code! Plus when if you have moving elements on the last VC beneath your current one, you can see it under the blur.

Hope this helps!

Feel free to checkout a demonstration project in my git!

Related Question