How do I animate in/out a gaussian blur effect in iOS?
Solution 1
With iOS 9 and above, you can animate the effect on visual effect views:
[UIView animateWithDuration:0.3 animations: ^ {
visualEffectView.effect = blurEffect;
} completion:nil];
This will achieve the expected effect of animating the blur radius.
I think if you animate a crossfade from non-blurred view -> blurred view, it will be pleasing enough. You need to animate the display of the blur view on top of the non-blurred view.
Let's assume blurView
is the view containing the blur effect.
Here are two examples of how to accomplish an animated transition:
[UIView transitionWithView:self.view duration:0.3 options:UIViewAnimationOptionTransitionCrossDissolve animations: ^ {
[self.view addSubview:blurView];
} completion:nil];
Here I assume blurView
has already been set up, and just transition the adding as subview in an animation.
You can also achieve this in a different way:
blurView.alpha = 0.0f;
[UIView animateWithDuration:0.3 animations: ^ {
blurView.alpha = 1.0f;
} completion:nil];
Here, I make the blur view transparent and animate its appearance. Notice that using this method will not work with hidden
, so you want to use the alpha
property.
If you wish to control the blur amount interactively, here is an example on how to achieve:
First create an image of the view you want to blur:
UIGraphicsBeginImageContextWithOptions(nonBlurredView.bounds.size, NO, self.view.window.screen.scale);
[nonBlurredView drawViewHierarchyInRect:nonBlurredView.bounds afterScreenUpdates:NO];
UIImage *snapshotImage = UIGraphicsGetImageFromCurrentImageContext();
Keep this image. You only want to redraw in a similar fashion if the view has been changed. Otherwise, you should reuse this image, as it is expensive to draw the hierarchy.
Now, when you need to blur, use the following code:
CIImage *imageToBlur = [CIImage imageWithCGImage:snapshotImage.CGImage];
CIFilter *gaussianBlurFilter = [CIFilter filterWithName: @"CIGaussianBlur"];
[gaussianBlurFilter setValue:imageToBlur forKey: @"inputImage"];
[gaussianBlurFilter setValue:@10 forKey: @"inputRadius"]; //Here you input the blur radius - the larger, the
CIImage *resultImage = [gaussianBlurFilter valueForKey: @"outputImage"];
UIImage *endImage = [[UIImage alloc] initWithCIImage:resultImage];
The inputRadius
input gives sets up the amount of blur performed. If you animate this, you will achieve an interactive feel.
But I still think the former method is much easier and will feel as good to the user.
Solution 2
This is easy on OS X, where one view can CIFilter an effect onto the view behind it, and where the processor is powerful and fast. On iOS, however, there are no view-compositing filters, and blurring takes some time: you can't do it repeatedly, live, as fast as the animation's frames. Therefore some form of compromise is needed.
If you know far enough in advance what it is that you want to blur, however, you can prepare a series of images using a gradation of blurring: no blurring, a little blurring, a little more blurring, and so on. Then you assemble the images and "play" them as the "frames" of an animated UIImageView.
That, however, is not what I would actually advise. What I would do is prepare the blur image and the non-blurred image and use a CIDissolveTransition to dissolve from the non-blurred to the blurred image. No, it isn't the same as gradually applying the blur, but it is computationally inexpensive and is the best "dissolve" effect in the toolbox.
Solution 3
I rarely suggest off the shelf solutions for coding problems. However, in this case, I would whole heartedly recommend LiveFrost. It's a really solid UIView subclass focused exclusively on replicating the popular iOS 7 gaussian blur effect.
I would also encourage you to read the author's blog regarding his design decisions for this class. I have done a lot of research specifically in this topic since the release of iOS 7, and I have literally ZERO COMPLAINTS about this code.
It even has blur animation out of the box! Good luck to you :)
Solution 4
I've developed a small project that uses GPUImage for live blurring with variable blur radius and framerate (MSLiveBlur) - this sounds like exactly what you need.
In the sample app I have a slider that increases / decreases the blur level in response to user action, as you mentioned in your question. If you wanted to animate it without user interaction, you could make a timer that slowly increases the blur radius until you reach the final value. You wouldn't be able to use CoreAnimation with this solution though.
[[MSLiveBlurView sharedInstance] setBlurInterval:0.2];
[[MSLiveBlurView sharedInstance] blurRect:someView.frame];
// Count x from 0 to your final radius
[MSLiveBlurView sharedInstance].blurRadius = x;
Solution 5
Here's a function that you can use to animate a blur with precomputed images. You say you're familiar with GPUImage so i used it (but it can works too with simple blur algorithm).
With this you can animate real blur in real time with a cost of 1-2% CPU
// configuration
let imageCount: Int = 10
let blurMax: Float = 40.0
// keep images in memory
var currentIdx: Int = 0
var imgs: [UIImage] = []
// func that create the precomputed images with GPUImage (call it in the viewDidLoad for example)
func initBlur() {
let blur = GaussianBlur()
let myImage: UIImage = UIImage(named: "your_image")!
let pictureInput = PictureInput(image: myImage)
let pictureOutput = PictureOutput()
pictureOutput.onlyCaptureNextFrame = false
pictureOutput.imageAvailableCallback = { image in
self.imgs.append(image)
}
pictureInput --> blur --> pictureOutput
for i in 0...imageCount {
blur.blurRadiusInPixels = (Float(i) / Float(imageCount)) * blurMax
pictureInput.processImage(synchronously: true)
}
}
// function that return the correct image from the image set with a blur percentage from a value between 0 and 1 where 0 = no blur and 1 full blurred.
func getBlurredImage(value: Float) -> UIImage? {
// return nil if there isn't precompiled images
if imgs.count == 0 {
return nil
}
// get the desired blurred image index
let idx = Int(value * Float(imageCount - 1))
// if the index changed, check the correctness of the index
if currentIdx != idx {
if idx < 0 {
currentIdx = 0
}
else if idx >= imgs.count {
currentIdx = imageCount - 1
}
else {
currentIdx = idx
}
}
return imgs[currentIdx]
}
And that's it, next you can use it for example with a timer or in a scrollViewDidScroll function like:
imageView.image = getBlurredImage(value: yOffset / height)
Related videos on Youtube
![Admin](/assets/logo_square_200-5d0d61d6853298bd2a4fe063103715b4daf2819fc21225efa21dfb93e61952ea.png)
Admin
Updated on February 28, 2020Comments
-
Admin over 4 years
For the whole iOS 7 feel, I want to apply a blur effect to a specific portion of the screen to obfuscate it, but I don't want to just throw the blur on instantly, I want to animate it in and animate it out so the user almost sees the blur effect being applied.
Almost as if in Photoshop you changed the gaussian blur value bit by bit from 0 to 10, instead of 0 to 10 in one go.
I've tried a few solutions to this, the most popular suggestion being to simply put the blurred view on top of a non-blurred view, and then lower the alpha value of the blurred view.
This works okay, but not very eye pleasing as there's no transition, it's just an overlay. Example:
What would be a better way to achieve such an effect? I'm familiar with GPUImage, but not sure how to accomplish it with that.
It'd also be great if I could control what percentage of the blur it is at, so it could be applied interactively from the user. (e.g.: user drags halfway, the blur is half applied, etc.)
-
Léo Natan about 10 yearsHow do you create the blur view and how do you add it to the view hierarchy? Please post some code.
-
silver_belt about 10 yearsI've gone the route of blurred view over the original, and animated the alpha on the blurred view. It looks and performs very well, so I'd recommend it unless you find a good reason not to do it.
-
Totumus Maximus about 10 years@aloha64 have you tried adjusting the 'RNFrostedSidebar'? github.com/rnystrom/RNFrostedSidebar
-
-
Sam about 10 yearsThis is a solid answer, but if you're seeking close to real time performance CIFilters will not cut it. You can get away with faking it in most places, but if you absolutely must have real time blur with animation (lucky you!) then you'll need to step down to the low level Accelerate framework and execute the box blur yourself.
-
Sam about 10 yearsAlso little trick: You can save some work on the blur by loading the UIImage as a JPG with quality level 1.
-
Léo Natan about 10 years@Sam Thanks for suggestions. I hate Apple for not providing their backdrop layer. In most cases of blur, I just use a toolbar with dark style and tint it to what I need. What the author describes in his blog is exactly what Apple does internally. They even have more room for optimizations because they have raw access to the QuartzCore scene and render buffer.
-
joakim over 9 yearsI like your template of 1) get an image of the view , 2) apply the blur and 3) animate it on top of another view. It's how I approach blurring too. I'd like to add that you could use the Accelerate framework for the blur to avoid the use of CIFilter. developer.apple.com/library/ios/samplecode/UIImageEffects. Oh and of course as of iOS 8 we can now use UIVisualEffectView and friends
-
Trev14 over 5 yearsAs someone who pays close attention to Apple's UI, I wouldn't crossfade in a blur on top since it feels cheap and doesn't give the best illusion of blurring the screen's current contents. Apple wouldn't do it :)
-
Léo Natan over 5 years@Trev14 this answer is old and I have already provided a solution that doesn’t cross fade for modern iOS.