Как я могу создать эффект, похожий на вид размытия iOS 7?

Я пытаюсь реплицировать этот размытый фон из Apple, публично выпущенного экрана примера iOS 7:

iOS 7 Control Center screenshot

Этот вопрос предлагает применить фильтр CI к содержанию ниже, но это совершенно другой подход. Очевидно, что iOS 7 не учитывает содержание нижеприведенных представлений по многим причинам:

  • Выполнение грубого тестирования, снятие скриншота из приведенных ниже представлений и применение фильтра CIGaussianBlur с достаточно большим радиусом для имитации стирания iOS 7 размытия занимает 1-2 секунды даже на симуляторе.
  • Взгляд размытия iOS 7 способен размывать динамические представления, такие как видео или анимации, без заметного запаздывания.

Кто-нибудь может предположить, какие рамки они могут использовать для создания этого эффекта, и если можно создать аналогичный эффект с текущими общедоступными API?

Изменить: (из комментария) Мы точно не знаем, как Apple это делает, но есть ли какие-либо базовые предположения, которые мы можем сделать? Мы можем предположить, что они используют аппаратное обеспечение, верно?

Является ли эффект самодостаточным в каждом представлении, так что эффект фактически не знает, что за ним? Или, в зависимости от того, как работает размытие, следует учитывать содержание за размытием?

Если содержание, лежащее в основе эффекта, актуально, можем ли мы предположить, что Apple получает "фид" нижеприведенного содержания и непрерывно визуализирует их с размытием?

Ответ 1

Зачем беспокоиться о повторении эффекта? Просто нарисуйте UIToolbar за своим видом.

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

Ответ 2

Apple выпустила код в WWDC как категорию в UIImage, которая включает эту функциональность, если у вас есть аккаунт разработчика, вы можете захватить категорию UIImage (и остальную часть кода примера), перейдя по этой ссылке: https://developer.apple.com/wwdc/schedule/ и просмотрите раздел 226 и нажмите на детали. Я еще не играл с ним, но я думаю, что эффект будет намного медленнее на iOS 6, есть некоторые улучшения для iOS 7, которые делают захват начального снимка экрана, который используется в качестве вклада в размытие намного быстрее.

Прямая ссылка: https://developer.apple.com/downloads/download.action?path=wwdc_2013/wwdc_2013_sample_code/ios_uiimageeffects.zip

Ответ 3

На самом деле, я бы сказал, это было бы довольно просто. Вероятно, он не будет работать или будет выглядеть точно так же, как Apple, но может быть очень близко.

Прежде всего, вам нужно будет определить CGRect UIView, который вы будете представлять. Как только вы определите, что вам просто нужно захватить изображение части пользовательского интерфейса, чтобы оно могло быть размытым. Что-то вроде этого...

- (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, предоставленную здесь, вы получите гауссовское размытие, которое очень похоже на размытие в 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

Теперь, когда вы вычисляете область экрана, чтобы размыть ее, передав ее в категорию размытия и получив поврежденную UIImage, теперь все, что осталось, это установить это размытое изображение в качестве фона представления, которое вы будете Предъявление. Как я уже сказал, это не будет идеальным сочетанием того, что делает Apple, но оно все равно выглядит довольно круто.

Надеюсь, что это поможет.

Ответ 4

iOS8 ответил на эти вопросы.

UIVisualEffect

- (instancetype)initWithEffect:(UIVisualEffect *)effect

или Swift:

init(effect effect: UIVisualEffect)

Ответ 5

Я только написал свой маленький подкласс UIView, который имеет возможность производить размытие iOS 7 на любом пользовательском представлении. Он использует UIToolbar, но безопасным способом для изменения его кадров, границ, цвета и альфы с анимацией в реальном времени.

Пожалуйста, дайте мне знать, если вы заметили какие-либо проблемы.

https://github.com/ivoleko/ILTranslucentView

ILTranslucentView examples

Ответ 6

Есть слухи, которые утверждали инженеры Apple, чтобы сделать этот исполнитель прямо из буфера gpu, что вызывает проблемы с безопасностью, поэтому для этого еще нет публичного API.

Ответ 7

Вот очень простой способ сделать это: https://github.com/JagCesar/iOS-blur

Просто скопируйте слой UIToolbar, и все готово, AMBlurView сделает это за вас. Ладно, он не такой расплывчатый, как центр управления, но достаточно размытый.

Помните, что iOS7 находится под NDA.

Ответ 8

Вы можете найти свое решение от Apple 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];
}

Надеюсь, это поможет вам.

Ответ 9

Это решение, которое вы можете увидеть в vidios WWDC. Вы должны сделать Gaussian Blur, так что первое, что вам нужно сделать, это добавить новый .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;
    }

Ответ 10

В каждом ответе используется vImageBoxConvolve_ARGB8888, эта функция действительно, очень медленная, это нормально, если производительность не является приоритетным требованием, но если вы используете ее для перехода между двумя контроллерами View (например) этот подход означает время более 1 секунды или, может быть, больше, что очень плохо для пользовательского опыта вашего приложения.

Если вы предпочитаете оставить всю эту обработку изображений на GPU (и вы должны), вы можете получить гораздо лучший эффект, а также потрясающие времена округления 50 мс (предположим, что у вас есть время 1 секунда в первом подходе), поэтому, давайте сделаем это.

Сначала загрузите GPUImage Framework (лицензия BSD) здесь.

Затем добавьте следующие классы (.m и .h) из GPUImage (я не уверен, что это минимум, необходимый только для эффекта размытия)

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

  • Иос/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

Да, получил удовольствие от этого гораздо более быстрого подхода;)

Ответ 11

Вы можете попробовать использовать мое собственное представление, которое имеет возможность размыть фон. Он делает это, притворяясь с моментальным снимком фона и смазывая его, точно так же, как в коде Apple WWDC. Это очень просто использовать.

Я также сделал некоторое улучшение, чтобы подделать динамическое размытие, не теряя производительности. Фон моего представления - scrollView, который прокручивается вместе с представлением, таким образом, обеспечивает эффект размытия для остальной части супервизора.

См. пример и код мой GitHub