如何在iOS中设置/输出高斯模糊效果?

时间:2014-02-26 04:02:06

标签: ios objective-c cocoa-touch ios7 gpuimage

对于整个iOS 7的感觉,我想对屏幕的特定部分应用模糊效果来对其进行模糊处理,但我不想立即抛出模糊,我想要将其设置为动画并设置动画它出来,所以用户几乎看到了应用的模糊效果。

几乎就像在Photoshop中一样,你将高斯模糊值从0改为10,而不是一次性改为0到10。

我已经尝试了一些解决方案,最流行的建议是简单地将模糊视图放在非模糊视图的顶部,然后降低模糊视图的alpha值。

这可以好的,但不是很赏心悦目,因为没有过渡,它只是一个叠加。例如:

enter image description here

实现这种效果的更好方法是什么?我熟悉GPUImage,但不知道如何用它完成它。

如果我可以控制它的模糊百分比,那也很棒,所以它可以从用户交互式地应用。 (例如:用户拖动中途,模糊一半,等等。)

5 个答案:

答案 0 :(得分:44)

使用iOS 9及更高版本,您可以为视觉效果视图设置动画效果:

[UIView animateWithDuration:0.3 animations: ^ {
    visualEffectView.effect = blurEffect;
} completion:nil];

这将实现动画模糊半径的预期效果。


我认为如果您从非模糊视图中设置交叉渐变动画 - >模糊的看法,这将是令人愉快的。您需要在非模糊视图的顶部为模糊视图的显示设置动画。

我们假设blurView是包含模糊效果的视图。

以下是如何完成动画过渡的两个示例:

[UIView transitionWithView:self.view duration:0.3 options:UIViewAnimationOptionTransitionCrossDissolve animations: ^ {
    [self.view addSubview:blurView];
} completion:nil];

这里我假设已经设置了blurView,只是将动画中的添加转换为子视图。

您也可以通过不同的方式实现这一目标:

blurView.alpha = 0.0f;
[UIView animateWithDuration:0.3 animations: ^ {
    blurView.alpha = 1.0f;
} completion:nil];

在这里,我将模糊视图设为透明并为其外观设置动画。请注意,使用此方法不适用于hidden,因此您希望使用alpha属性。


如果您希望以交互方式控制模糊量,以下是如何实现的示例:

首先创建要模糊的视图的图像:

UIGraphicsBeginImageContextWithOptions(nonBlurredView.bounds.size, NO, self.view.window.screen.scale);
[nonBlurredView drawViewHierarchyInRect:nonBlurredView.bounds afterScreenUpdates:NO];
UIImage *snapshotImage = UIGraphicsGetImageFromCurrentImageContext();

保留此图片。如果视图已更改,您只想以类似的方式重绘。否则,您应该重复使用此图像,因为绘制层次结构的成本很高。

现在,当您需要模糊时,请使用以下代码:

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];

inputRadius输入可设置执行的模糊量。如果你动画这个,你将获得互动的感觉。

但我仍然认为前一种方法更容易,并且对用户也感觉良好。

答案 1 :(得分:6)

在OS X上这很容易,其中一个视图可以对其后面的视图产生影响,并且处理器功能强大且速度快。然而,在iOS上,没有视图合成滤镜,模糊需要一些时间:你不能像动画的帧那样重复,实时地进行。因此需要某种形式的妥协。

如果你事先知道你想要模糊的东西,你可以使用渐变的模糊来准备一系列图像:没有模糊,有点模糊,有点模糊,等等。然后你组装图像和"播放"他们作为"框架"一个动画的UIImageView。

然而,这不是我实际建议的。我要做的是准备模糊图像和非模糊图像,并使用CIDissolveTransition从非模糊图像溶解到模糊图像。不,它与逐渐应用模糊不同,但它在计算上很便宜并且是最好的解决方案。效果在工具箱中。

答案 2 :(得分:3)

我很少建议现成的解决方案来解决编码问题。但是,在这种情况下,我会全心全意地推荐LiveFrost。它是一个非常可靠的UIView子类,专门用于复制流行的iOS 7高斯模糊效果。

我还鼓励您阅读作者的博客,了解他对本课程的设计决策。自从iOS 7发布以来,我已经在这个主题上做了很多研究,我对这段代码实际上是零推荐。

它甚至还有开箱即用的模糊动画!祝你好运:))

答案 3 :(得分:3)

我开发了一个小项目,使用GPUImage进行实时模糊,可变模糊半径和帧率(MSLiveBlur) - 这听起来就像你需要的那样。

在示例应用程序中,我有一个滑块可以增加/减少模糊级别以响应用户操作,正如您在问题中提到的那样。如果您想在没有用户交互的情况下为其设置动画,您可以制作一个计时器,缓慢增加模糊半径,直到达到最终值。但是,您无法在此解决方案中使用CoreAnimation。

[[MSLiveBlurView sharedInstance] setBlurInterval:0.2];
[[MSLiveBlurView sharedInstance] blurRect:someView.frame];

// Count x from 0 to your final radius
[MSLiveBlurView sharedInstance].blurRadius = x;

答案 4 :(得分:1)

这是一个可用于使用预先计算的图像为模糊设置动画的功能。你说你熟悉GPUImage所以我用它(但它也适用于简单的模糊算法)。

通过此功能,您可以在实时中为真实模糊制作动画,费用为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]
}

就是这样,接下来你可以使用它作为计时器或 scrollViewDidScroll 功能,例如:

imageView.image = getBlurredImage(value: yOffset / height)