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
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.
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;
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)
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.
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 :)