首页 文章

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

提问于
浏览
216

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

iOS 7 Control Center screenshot

This question建议对以下内容应用CI过滤器,但是很明显,iOS 7不会捕获下面视图的内容,原因有很多:

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

  • iOS 7模糊视图能够模糊动态视图,例如视频或动画,没有明显的延迟 .

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

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

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

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

12 回答

  • 2

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

    myView.backgroundColor = [UIColor clearColor];
    UIToolbar* bgToolbar = [[UIToolbar alloc] initWithFrame:myView.frame];
    bgToolbar.barStyle = UIBarStyleDefault;
    [myView.superview insertSubview:bgToolbar belowSubview:myView];
    
  • 37

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

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

  • 24

    实际上我敢打赌,这样做很简单 . 它可能不会运行或看起来像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;
    }
    

    高斯模糊 - 推荐

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

    Box Blur

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

    @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
    

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

    希望能帮助到你 .

  • 64

    iOS8回答了这些问题 .

    UIVisualEffect

    - (instancetype)initWithEffect:(UIVisualEffect *)effect

    或斯威夫特:

    init(effect effect: UIVisualEffect)

  • 7

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

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

    https://github.com/ivoleko/ILTranslucentView

    ILTranslucentView examples

  • 7

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

  • 10

    这是您可以在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;
        }
    
  • 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];
    }
    

    希望这会帮助你 .

  • 133

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

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

    请记住,iOS7属于NDA .

  • 6

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

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

    首先下载GPUImage Framework(BSD Licensed)here .

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

    • GPUImage.h

    • GPUImageAlphaBlendFilter

    • GPUImageFilter

    • GPUImageFilterGroup

    • GPUImageGaussianBlurPositionFilter

    • GPUImageGaussianSelectiveBlurFilter

    • GPUImageLuminanceRangeFilter

    • GPUImageOutput

    • GPUImageTwoInputFilter

    • GLProgram

    • GPUImageBoxBlurFilter

    • GPUImageGaussianBlurFilter

    • GPUImageiOSBlurFilter

    • GPUImageSaturationFilter

    • GPUImageSolidColorGenerator

    • GPUImageTwoPassFilter

    • GPUImageTwoPassTextureSamplingFilter

    • iOS / 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
    

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

    AVFoundation CoreMedia CoreVideo OpenGLES

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

  • 4

    您可以尝试使用我的自定义视图,该视图具有模糊背景的功能 . 它通过伪造背景快照并模糊它来实现这一点,就像Apple的WWDC代码中的那样 . 它使用起来非常简单 .

    我也做了一些改进,以假冒动态模糊而不会失去性能 . 我的视图背景是一个scrollView,它随视图滚动,从而为其余的superview提供模糊效果 .

    请参阅my GitHub上的示例和代码

  • 19

    Core Background实现了所需的iOS 7效果 .

    https://github.com/justinmfischer/core-background

    免责声明:我是这个项目的作者

相关问题