如何生成类似于iOS 7模糊视图的效果?

Sno*_*man 218 graphics transparency ios

我试图从Apple公开发布的iOS 7示例屏幕中复制这个模糊的背景:

iOS 7 Control Center截图

这个问题建议对以下内容应用CI过滤器,但这是一种完全不同的方法.很明显iOS 7没有捕获下面视图的内容,原因有很多:

  1. 进行一些粗略测试,捕获下面视图的屏幕截图并应用具有足够大半径的CIGaussianBlur滤镜来模仿iOS 7的模糊样式需要1-2秒,即使在模拟器上也是如此.
  2. iOS 7模糊视图能够模糊动态视图,例如视频或动画,没有明显的延迟.

任何人都可以假设他们可以使用什么框架来创建这种效果,并且是否可以使用当前的公共API创建类似的效果?

编辑:(来自评论)我们并不完全知道Apple是如何做到的,但我们可以做出任何基本的假设吗?我们可以假设他们正在使用硬件,对吧?

效果是否在每个视图中都是自包含的,这样效果实际上并不知道它背后的含义是什么?或者,必须根据模糊的工作原理,考虑模糊背后的内容?

如果效果背后的内容是相关的,我们可以假设Apple正在接收下面内容的"提要"并持续呈现它们模糊吗?

小智 133

为什么要复制效果呢?只需在视图后面绘制UIToolbar即可.

myView.backgroundColor = [UIColor clearColor];
UIToolbar* bgToolbar = [[UIToolbar alloc] initWithFrame:myView.frame];
bgToolbar.barStyle = UIBarStyleDefault;
[myView.superview insertSubview:bgToolbar belowSubview:myView];
Run Code Online (Sandbox Code Playgroud)

  • 我本周在他们的Tech Talks实验室中由Apple UIKit工程师运行这种方法.虽然他当然不会认可这种方法,但他认识到这种方法需要效果和缺乏真正的公共API,并且说这种方法现在是"最不邪恶"的选择,而且写得相当安全.具体来说,他说不要试图对这个工具栏/视图的"框架"或"变换"做任何动画或类似的事情,否则就会发生坏事.他还强烈建议就此提交Radar bug报告,在内部构建一个案例,以便我们可以获得真正的公共API来实现这一效果! (116认同)
  • 有意思......看起来帐号@ user2342340的创建只是为了匿名回答这个问题.让你想知道这不是一个非官方的帖子,一个比我们其他人更了解这些事情的人:) (43认同)
  • 我不同意crizzwald.我不认为这是对APple将要做的期望规则的一个很好的解释. (8认同)
  • 这在运行iOS 7的iPhone 4上不起作用.可能是因为在iPhone 4上,由于GPU功率太低,系统不会为它的"UITabBar"本身添加通常的模糊效果. (4认同)
  • 如何让它变成非白色?如果我更改工具栏的背景颜色,则不会显示模糊. (2认同)

Fre*_*nda 64

Apple在WWDC上发布了包含此功能的UIImage类别代码,如果您有开发者帐户,可以通过以下链接获取UIImage类别(以及示例代码的其余部分):https://developer.apple. com/wwdc/schedule /并浏览226节并点击详细信息.我还没玩过它,但我认为在iOS 6上效果会慢得多,iOS 7有一些增强功能,可以抓住初始屏幕截图,用作模糊输入的速度要快得多.

直接链接:https://developer.apple.com/downloads/download.action?path = wwwdc_2013/wdddd_2013_sample_code /ios_uiimageeffects.zip


Jer*_*Fox 37

实际上我敢打赌,这样做很简单.它可能不会运行或看起来像Apple正在发生的那样但可能非常接近.

首先,您需要确定您将要呈现的UIView的CGRect.一旦你确定你只需要抓取UI部分的图像就可以模糊.像这样......

- (UIImage*)getBlurredImage {
    // You will want to calculate this in code based on the view you will be presenting.
    CGSize size = CGSizeMake(200,200);

    UIGraphicsBeginImageContext(size);
    [view drawViewHierarchyInRect:(CGRect){CGPointZero, w, h} afterScreenUpdates:YES]; // view is the view you are grabbing the screen shot of. The view that is to be blurred.
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    // Gaussian Blur
    image = [image applyLightEffect];

    // Box Blur
    // image = [image boxblurImageWithBlur:0.2f];

    return image;
}
Run Code Online (Sandbox Code Playgroud)

高斯模糊 - 推荐

使用此处UIImage+ImageEffects提供的类别Apple ,您将获得高斯模糊,看起来非常像iOS 7中的模糊.

盒子模糊

您还可以使用以下boxBlurImageWithBlur:UIImage类别使用框模糊.这是基于你可以在这里找到的algorythem .

@implementation UIImage (Blur)

-(UIImage *)boxblurImageWithBlur:(CGFloat)blur {
    if (blur < 0.f || blur > 1.f) {
        blur = 0.5f;
    }
    int boxSize = (int)(blur * 50);
    boxSize = boxSize - (boxSize % 2) + 1;

    CGImageRef img = self.CGImage;

    vImage_Buffer inBuffer, outBuffer;

    vImage_Error error;

    void *pixelBuffer;

    CGDataProviderRef inProvider = CGImageGetDataProvider(img);
    CFDataRef inBitmapData = CGDataProviderCopyData(inProvider);

    inBuffer.width = CGImageGetWidth(img);
    inBuffer.height = CGImageGetHeight(img);
    inBuffer.rowBytes = CGImageGetBytesPerRow(img);

    inBuffer.data = (void*)CFDataGetBytePtr(inBitmapData);

    pixelBuffer = malloc(CGImageGetBytesPerRow(img) * CGImageGetHeight(img));

    if(pixelBuffer == NULL)
        NSLog(@"No pixelbuffer");

    outBuffer.data = pixelBuffer;
    outBuffer.width = CGImageGetWidth(img);
    outBuffer.height = CGImageGetHeight(img);
    outBuffer.rowBytes = CGImageGetBytesPerRow(img);

    error = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);

    if (error) {
        NSLog(@"JFDepthView: error from convolution %ld", error);
    }

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef ctx = CGBitmapContextCreate(outBuffer.data,
                                         outBuffer.width,
                                         outBuffer.height,
                                         8,
                                         outBuffer.rowBytes,
                                         colorSpace,
                                         kCGImageAlphaNoneSkipLast);
    CGImageRef imageRef = CGBitmapContextCreateImage (ctx);
    UIImage *returnImage = [UIImage imageWithCGImage:imageRef];

    //clean up
    CGContextRelease(ctx);
    CGColorSpaceRelease(colorSpace);

    free(pixelBuffer);
    CFRelease(inBitmapData);

    CGImageRelease(imageRef);

    return returnImage;
}

@end
Run Code Online (Sandbox Code Playgroud)

现在您正在计算要模糊的屏幕区域,将其传递到模糊类别并接收已模糊的UIImage背面,现在剩下的就是将该模糊图像设置为您将要呈现的视图的背景.就像我说的那样,这对于Apple正在做的事情来说不是一个完美的匹配,但它看起来仍然很酷.

希望能帮助到你.


Ada*_*ite 24

iOS8回答了这些问题.

UIVisualEffect

- (instancetype)initWithEffect:(UIVisualEffect *)effect

或斯威夫特:

init(effect effect: UIVisualEffect)


Ivo*_*eko 19

我刚刚编写了我的UIView的小子类,它能够在任何自定义视图上生成本机iOS 7模糊.它使用UIToolbar但以安全的方式使用实时动画更改它的边框,边界,颜色和alpha.

如果您发现任何问题,请告诉我.

https://github.com/ivoleko/ILTranslucentView

ILTranslucentView示例

  • 它对新的iOS 7.0.3下载有何反应?使用此技术的其他类不再正确呈现:[ (4认同)

Cod*_*y C 10

苹果公司的工程师声称,为了提高性能,他们正在直接从gpu缓冲区中读取这会引发安全问题,这就是为什么没有公共API可以做到这一点的原因.

  • 这篇文章是谣言的来源吗?:) (24认同)
  • 谣言可能是无聊的.iOS上的OpenGL ES 2.0允许您读取和写入帧缓冲区,而不会有任何安全风险.使用GLSL着色器完成模糊,这就是它快速运行的原因. (11认同)
  • 如果这是真的那么 - 到目前为止 - 是迄今为止最糟糕的解决方案. (6认同)
  • 它仅在看到性能问题的设备上删除. (3认同)
  • aaaaa和模糊已从iOS 7中删除. (2认同)

Sim*_*mon 7

这是一个非常简单的方法:https://github.com/JagCesar/iOS-blur

只需复制UIToolbar图层就可以了,AMBlurView会为您完成.好吧,它不像控制中心那么模糊,但是模糊不清.

请记住,iOS7属于NDA.


小智 7

您可以在此页面中找到Apple's DEMO的解决方案: WWDC 2013,查找并下载UIImageEffects示例代码.

然后用@Jeremy Fox的代码.我改成了

- (UIImage*)getDarkBlurredImageWithTargetView:(UIView *)targetView
{
    CGSize size = targetView.frame.size;

    UIGraphicsBeginImageContext(size);
    CGContextRef c = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(c, 0, 0);
    [targetView.layer renderInContext:c]; // view is the view you are grabbing the screen shot of. The view that is to be blurred.
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return [image applyDarkEffect];
}
Run Code Online (Sandbox Code Playgroud)

希望这会帮助你.


Mar*_*coz 7

这是您可以在WWDC的视频中看到的解决方案.你必须做一个高斯模糊,所以你要做的第一件事是添加一个新的.m和.h文件与我在这里写的代码,然后你必须制作和屏幕拍摄,使用所需的效果和将它添加到你的视图,然后你的UITable UIView或者必须透明,你可以使用applyBlurWithRadius来存档所需的效果,这个调用适用于任何UIImage.

最后,模糊图像将是背景,上面的其他控件必须是透明的.

为此,您必须添加下一个库:

Acelerate.framework,UIKit.framework,CoreGraphics.framework

我希望你喜欢它.

快乐的编码.

    //Screen capture.
    UIGraphicsBeginImageContext(self.view.bounds.size);

    CGContextRef c = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(c, 0, 0);
    [self.view.layer renderInContext:c];

    UIImage* viewImage = UIGraphicsGetImageFromCurrentImageContext();
    viewImage = [viewImage applyLightEffect];

    UIGraphicsEndImageContext();

    //.h FILE
    #import <UIKit/UIKit.h>

    @interface UIImage (ImageEffects)

   - (UIImage *)applyLightEffect;
   - (UIImage *)applyExtraLightEffect;
   - (UIImage *)applyDarkEffect;
   - (UIImage *)applyTintEffectWithColor:(UIColor *)tintColor;

   - (UIImage *)applyBlurWithRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage;

   @end

    //.m FILE
    #import "cGaussianEffect.h"
    #import <Accelerate/Accelerate.h>
    #import <float.h>


     @implementation UIImage (ImageEffects)


    - (UIImage *)applyLightEffect
    {
        UIColor *tintColor = [UIColor colorWithWhite:1.0 alpha:0.3];
        return [self applyBlurWithRadius:1 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil];
    }


    - (UIImage *)applyExtraLightEffect
    {
        UIColor *tintColor = [UIColor colorWithWhite:0.97 alpha:0.82];
        return [self applyBlurWithRadius:1 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil];
    }


    - (UIImage *)applyDarkEffect
    {
        UIColor *tintColor = [UIColor colorWithWhite:0.11 alpha:0.73];
        return [self applyBlurWithRadius:1 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil];
    }


    - (UIImage *)applyTintEffectWithColor:(UIColor *)tintColor
    {
        const CGFloat EffectColorAlpha = 0.6;
        UIColor *effectColor = tintColor;
        int componentCount = CGColorGetNumberOfComponents(tintColor.CGColor);
        if (componentCount == 2) {
            CGFloat b;
            if ([tintColor getWhite:&b alpha:NULL]) {
                effectColor = [UIColor colorWithWhite:b alpha:EffectColorAlpha];
            }
        }
        else {
            CGFloat r, g, b;
            if ([tintColor getRed:&r green:&g blue:&b alpha:NULL]) {
                effectColor = [UIColor colorWithRed:r green:g blue:b alpha:EffectColorAlpha];
            }
        }
        return [self applyBlurWithRadius:10 tintColor:effectColor saturationDeltaFactor:-1.0 maskImage:nil];
    }


    - (UIImage *)applyBlurWithRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage
    {
        if (self.size.width < 1 || self.size.height < 1) {
            NSLog (@"*** error: invalid size: (%.2f x %.2f). Both dimensions must be >= 1: %@", self.size.width, self.size.height, self);
            return nil;
        }
        if (!self.CGImage) {
            NSLog (@"*** error: image must be backed by a CGImage: %@", self);
            return nil;
        }
        if (maskImage && !maskImage.CGImage) {
            NSLog (@"*** error: maskImage must be backed by a CGImage: %@", maskImage);
            return nil;
        }

        CGRect imageRect = { CGPointZero, self.size };
        UIImage *effectImage = self;

        BOOL hasBlur = blurRadius > __FLT_EPSILON__;
        BOOL hasSaturationChange = fabs(saturationDeltaFactor - 1.) > __FLT_EPSILON__;
        if (hasBlur || hasSaturationChange) {
            UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
            CGContextRef effectInContext = UIGraphicsGetCurrentContext();
            CGContextScaleCTM(effectInContext, 1.0, -1.0);
            CGContextTranslateCTM(effectInContext, 0, -self.size.height);
            CGContextDrawImage(effectInContext, imageRect, self.CGImage);

            vImage_Buffer effectInBuffer;
            effectInBuffer.data     = CGBitmapContextGetData(effectInContext);
            effectInBuffer.width    = CGBitmapContextGetWidth(effectInContext);
            effectInBuffer.height   = CGBitmapContextGetHeight(effectInContext);
            effectInBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectInContext);

            UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
            CGContextRef effectOutContext = UIGraphicsGetCurrentContext();
            vImage_Buffer effectOutBuffer;
            effectOutBuffer.data     = CGBitmapContextGetData(effectOutContext);
            effectOutBuffer.width    = CGBitmapContextGetWidth(effectOutContext);
            effectOutBuffer.height   = CGBitmapContextGetHeight(effectOutContext);
            effectOutBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectOutContext);

            if (hasBlur) {
                CGFloat inputRadius = blurRadius * [[UIScreen mainScreen] scale];
                NSUInteger radius = floor(inputRadius * 3. * sqrt(2 * M_PI) / 4 + 0.5);
                if (radius % 2 != 1) {
                    radius += 1;
                }
                vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
                vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
                vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
            }
            BOOL effectImageBuffersAreSwapped = NO;
            if (hasSaturationChange) {
                CGFloat s = saturationDeltaFactor;
                CGFloat floatingPointSaturationMatrix[] = {
                    0.0722 + 0.9278 * s,  0.0722 - 0.0722 * s,  0.0722 - 0.0722 * s,  0,
                    0.7152 - 0.7152 * s,  0.7152 + 0.2848 * s,  0.7152 - 0.7152 * s,  0,
                    0.2126 - 0.2126 * s,  0.2126 - 0.2126 * s,  0.2126 + 0.7873 * s,  0,
                                  0,                    0,                    0,  1,
                };
                const int32_t divisor = 256;
                NSUInteger matrixSize = sizeof(floatingPointSaturationMatrix)/sizeof(floatingPointSaturationMatrix[0]);
                int16_t saturationMatrix[matrixSize];
                for (NSUInteger i = 0; i < matrixSize; ++i) {
                    saturationMatrix[i] = (int16_t)roundf(floatingPointSaturationMatrix[i] * divisor);
                }
                if (hasBlur) {
                    vImageMatrixMultiply_ARGB8888(&effectOutBuffer, &effectInBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags);
                    effectImageBuffersAreSwapped = YES;
                }
                else {
                    vImageMatrixMultiply_ARGB8888(&effectInBuffer, &effectOutBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags);
                }
            }
            if (!effectImageBuffersAreSwapped)
                effectImage = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();

            if (effectImageBuffersAreSwapped)
                effectImage = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
        }

        UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
        CGContextRef outputContext = UIGraphicsGetCurrentContext();
        CGContextScaleCTM(outputContext, 1.0, -1.0);
        CGContextTranslateCTM(outputContext, 0, -self.size.height);

        CGContextDrawImage(outputContext, imageRect, self.CGImage);

        if (hasBlur) {
            CGContextSaveGState(outputContext);
            if (maskImage) {
                CGContextClipToMask(outputContext, imageRect, maskImage.CGImage);
            }
            CGContextDrawImage(outputContext, imageRect, effectImage.CGImage);
            CGContextRestoreGState(outputContext);
        }

        if (tintColor) {
            CGContextSaveGState(outputContext);
            CGContextSetFillColorWithColor(outputContext, tintColor.CGColor);
            CGContextFillRect(outputContext, imageRect);
            CGContextRestoreGState(outputContext);
        }

        UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();

        return outputImage;
    }
Run Code Online (Sandbox Code Playgroud)


D33*_*6h7 6

这里的每个响应都是使用vImageBoxConvolve_ARGB8888这个函数真的非常慢,很好,如果性能不是高优先级要求,但是如果你使用它来在两个视图控制器之间进行转换(例如)这种方法意味着时间超过1第二个或更多,这对您的应用程序的用户体验非常不利.

如果你更喜欢把所有这些图像处理留给GPU(你应该),你可以获得更好的效果,也可以获得更好的效果,并且可以在50ms内完成(假设你在第一种方法中有1秒的时间),所以,让我们这样做.

首先在这里下载GPUImage Framework(BSD Licensed).

接下来,从GPUImage添加以下类(.m和.h)(我不确定这些是模糊效果所需的最小值)

  • GPUImage.h
  • GPUImageAlphaBlendFilter
  • GPUImageFilter
  • GPUImageFilterGroup
  • GPUImageGaussianBlurPositionFilter
  • GPUImageGaussianSelectiveBlurFilter
  • GPUImageLuminanceRangeFilter
  • GPUImageOutput
  • GPUImageTwoInputFilter
  • GLProgram
  • GPUImageBoxBlurFilter
  • GPUImageGaussianBlurFilter
  • GPUImageiOSBlurFilter
  • GPUImageSaturationFilter
  • GPUImageSolidColorGenerator
  • GPUImageTwoPassFilter
  • GPUImageTwoPassTextureSamplingFilter

  • 式IO/GPUImage-Prefix.pch

  • iOS版/ GPUImageContext
  • iOS版/ GPUImageMovieWriter
  • iOS版/ GPUImagePicture
  • iOS版/ GPUImageView

接下来,在UIImage上创建一个类别,它将为现有的UIImage添加模糊效果:

#import "UIImage+Utils.h"

#import "GPUImagePicture.h"
#import "GPUImageSolidColorGenerator.h"
#import "GPUImageAlphaBlendFilter.h"
#import "GPUImageBoxBlurFilter.h"

@implementation UIImage (Utils)

- (UIImage*) GPUBlurredImage
{
    GPUImagePicture *source =[[GPUImagePicture alloc] initWithImage:self];

    CGSize size = CGSizeMake(self.size.width * self.scale, self.size.height * self.scale);

    GPUImageBoxBlurFilter *blur = [[GPUImageBoxBlurFilter alloc] init];
    [blur setBlurRadiusInPixels:4.0f];
    [blur setBlurPasses:2.0f];
    [blur forceProcessingAtSize:size];
    [source addTarget:blur];

    GPUImageSolidColorGenerator * white = [[GPUImageSolidColorGenerator alloc] init];

    [white setColorRed:1.0f green:1.0f blue:1.0f alpha:0.1f];
    [white forceProcessingAtSize:size];

    GPUImageAlphaBlendFilter * blend = [[GPUImageAlphaBlendFilter alloc] init];
    blend.mix = 0.9f;

    [blur addTarget:blend];
    [white addTarget:blend];

    [blend forceProcessingAtSize:size];
    [source processImage];

    return [blend imageFromCurrentlyProcessedOutput];
}

@end
Run Code Online (Sandbox Code Playgroud)

最后,将以下框架添加到您的项目中:

AVFoundation CoreMedia CoreVideo OpenGLES

是的,通过这种更快的方法获得乐趣;)