web-dev-qa-db-fra.com

Comment puis-je produire un effet similaire à la vue flou iOS 7?

J'essaie de reproduire cet arrière-plan flou à partir de l'exemple d'écran iOS 7 d'Apple publié publiquement:

iOS 7 Control Center screenshot

Cette question suggère d'appliquer un filtre d'EC au contenu ci-dessous, mais c'est une approche totalement différente. Il est évident que iOS 7 ne capture pas le contenu des vues ci-dessous, pour plusieurs raisons:

  1. Effectuez des tests approximatifs, capturez une capture d'écran des vues ci-dessous et appliquez un filtre CIGaussianBlur avec un rayon suffisamment grand pour imiter le style de flou d'iOS 7 prend 1 à 2 secondes, même sur un simulateur.
  2. La vue floue d’IOS 7 est capable de brouiller des vues dynamiques, telles que des vidéos ou des animations, sans retard notable.

Quelqu'un peut-il imaginer quels frameworks ils pourraient utiliser pour créer cet effet et s'il est possible de créer un effet similaire avec les API publiques actuelles?

Edit: (from comment) Nous ne savons pas exactement comment Apple le fait, mais y a-t-il des hypothèses de base que nous pouvons faire? Nous pouvons supposer qu'ils utilisent du matériel, non?

L'effet est-il autonome dans chaque vue, de telle sorte que l'effet ne sait pas ce qu'il y a derrière? Ou doit-on, en fonction de la manière dont fonctionne le flou, tenir compte du contenu du flou?

Si le contenu derrière l’effet est pertinent, pouvons-nous supposer que Apple reçoit un "flux" du contenu ci-dessous et le restitue en permanence avec un flou?

218
Snowman

Pourquoi s'embêter à reproduire l'effet? Dessinez simplement une barre UIToolbar derrière votre vue.

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

Apple a publié du code sur WWDC en tant que catégorie sur UIImage qui inclut cette fonctionnalité. Si vous disposez d’un compte développeur, vous pouvez récupérer la catégorie UIImage (et le reste de l’exemple de code) en cliquant sur ce lien: https: // developer.Apple.com/wwdc/schedule/ et parcourant l'article 226 en cliquant sur les détails. Je n'y ai pas encore joué, mais je pense que l'effet sera beaucoup plus lent sur iOS 6; certaines améliorations apportées à iOS 7 rendent la capture de la capture d'écran initiale utilisée comme entrée du flou beaucoup plus rapide.

Lien direct: https://developer.Apple.com/downloads/download.action?path=wwdc_2013/wwdc_2013_sample_code/ios_uiimageeffects.Zip

64
FreaknBigPanda

En fait, je parierais que ce serait plutôt simple à réaliser. Cela ne fonctionnerait probablement pas ou ne ressemblerait pas exactement à ce que Apple se passe, mais pourrait être très proche.

Tout d’abord, vous devez déterminer le CGRect de UIView que vous présenterez. Une fois que vous avez déterminé que vous auriez simplement besoin de saisir une image de la partie de l'interface utilisateur afin qu'elle puisse être floue. Quelque chose comme ça...

- (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;
}

Flou gaussien - Recommandé

En utilisant le UIImage+ImageEffects Catégorie fourni par Apple ici , vous obtiendrez un flou gaussien qui ressemble beaucoup au flou sous iOS 7.

Flou de boîte

Vous pouvez également utiliser un flou de zone en utilisant la catégorie suivante boxBlurImageWithBlur: UIImage. Ceci est basé sur un algorithme que vous pouvez trouver ici .

@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

Maintenant que vous calculez le flou de la zone d’écran, que vous la passez dans la catégorie Flou et que vous recevez une image UII qui a été floue, il ne vous reste plus qu’à définir cette image floue comme arrière-plan de la vue que vous allez présenter. Comme je l'ai dit, cela ne sera pas parfaitement assorti à ce que Apple est en train de faire, mais cela devrait quand même avoir l'air cool.

J'espère que ça aide.

37
Jeremy Fox

iOS8 a répondu à ces questions.

UIVisualEffect

- (instancetype)initWithEffect:(UIVisualEffect *)effect

ou rapide:

init(effect effect: UIVisualEffect)

24
Adam Waite

Je viens d'écrire ma petite sous-classe d'UIView capable de produire du flou iOS 7 natif sur n'importe quel affichage personnalisé. Il utilise UIToolbar mais de manière sécurisée pour changer son cadre, ses limites, sa couleur et son alpha avec une animation en temps réel.

S'il vous plaît laissez-moi savoir si vous remarquez des problèmes.

https://github.com/ivoleko/ILTranslucentView

ILTranslucentView examples

19
Ivo Leko

Selon une rumeur, Apple ingénieurs ont prétendu qu'ils lisaient directement à partir du tampon gpu, ce qui pose des problèmes de sécurité. C'est pourquoi il n'y a pas encore d'API publique.

10
Cody C

C'est une solution que vous pouvez voir dans les vidios de la WWDC. Vous devez faire un flou gaussien. La première chose à faire est donc d’ajouter un nouveau fichier .m et .h avec le code que j’écris ici. Vous devez ensuite créer et faire une capture d’écran, utiliser ajoutez-le à votre vue, puis votre UIView UITable ou ce qui doit toujours être transparent, vous pouvez jouer avec applyBlurWithRadius, pour archiver l'effet souhaité, cet appel fonctionne avec n'importe quel UIImage.

À la fin, l'image estompée sera le fond et le reste des contrôles ci-dessus doit être transparent.

Pour que cela fonctionne, vous devez ajouter les bibliothèques suivantes:

Acelerate.framework, UIKit.framework, CoreGraphics.framework

J'espère que tu aimes.

Bonne codage.

    //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;
    }

Vous pouvez trouver votre solution à partir de la DEMO d’Apple sur cette page: WWDC 201 , découvrez et téléchargez le code exemple UIImageEffects.

Puis avec le code de @Jeremy Fox. Je l'ai changé pour

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

J'espère que cela vous aidera.

7
Senry

Voici un moyen très simple de le faire: https://github.com/JagCesar/iOS-blur

Copiez simplement la couche de UIToolbar et vous avez terminé, AMBlurView le fait pour vous. D'accord, ce n'est pas aussi flou que le centre de contrôle, mais c'est assez flou.

Rappelez-vous que iOS7 est sous NDA.

7
Simon

Chaque réponse ici utilise vImageBoxConvolve_ARGB8888 cette fonction est vraiment très lente, cela va, si les performances ne sont pas une priorité élevée, mais si vous êtes En l'utilisant pour la transition entre deux contrôleurs de vue (par exemple), cette approche signifie un temps supérieur à 1 seconde ou peut-être plus, ce qui est très néfaste pour l'expérience utilisateur de votre application.

Si vous préférez laisser tout ce traitement d’images au GPU (et vous devriez), vous pouvez obtenir un effet bien meilleur ainsi que des temps impressionnants, avec des temps arrondis de 50 ms (en supposant que vous avez un temps de 1 seconde lors de la première approche), alors faisons-le .

Commencez par télécharger le cadre GPUImage (licence BSD) ici .

Ensuite, ajoutez les classes suivantes (.m et .h) à partir de GPUImage (je ne suis pas sûr qu'il s'agisse du minimum nécessaire pour l'effet de flou uniquement).

  • 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

Ensuite, créez une catégorie sur UIImage, qui ajoutera un effet de flou à un UIImage existant:

#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

Enfin, ajoutez les cadres suivants à votre projet:

AVFoundation CoreMedia CoreVideo OpenGLES

Oui, je me suis amusé avec cette approche beaucoup plus rapide;)

6
D33pN16h7

Vous pouvez essayer d’utiliser ma vue personnalisée, qui permet de flouter l’arrière-plan. Pour ce faire, il simule un instantané de l'arrière-plan et le rend flou, tout comme celui du code WWDC d'Apple. C'est très simple à utiliser.

J'ai également apporté quelques améliorations pour simuler le flou dynamique sans perdre la performance. L'arrière-plan de ma vue est une scrollView qui défile avec la vue, fournissant ainsi l'effet de flou pour le reste de la vue d'ensemble.

Voir l'exemple et le code sur mon GitHub

4
Byte

Core Background implémente l'effet iOS 7 souhaité.

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

Disclaimer: Je suis l'auteur de ce projet

2
Justin Fischer