399 votes

Créer une vue superposée floue

Dans l'application Musique du nouveau iOS, nous pouvons voir une pochette d'album derrière une vue qui la floute.

Comment peut-on accomplir quelque chose de ce genre ? J'ai lu la documentation, mais je n'ai rien trouvé à ce sujet.

0 votes

596voto

Joey Points 2141

Vous pouvez utiliser UIVisualEffectView pour obtenir cet effet. Il s'agit d'une API native qui a été optimisée pour les performances et une longue durée de vie de la batterie, de plus, elle est facile à implémenter.

Swift:

//appliquer le flou uniquement si l'utilisateur n'a pas désactivé les effets de transparence
if !UIAccessibility.isReduceTransparencyEnabled {
    view.backgroundColor = .clear

    let blurEffect = UIBlurEffect(style: .dark)
    let blurEffectView = UIVisualEffectView(effect: blurEffect)
    //remplir toujours la vue
    blurEffectView.frame = self.view.bounds
    blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]

    view.addSubview(blurEffectView) //si vous avez plus de UIViews, utilisez une API insertSubview pour le placer où nécessaire
} else {
    view.backgroundColor = .black
}

Objective-C:

//appliquer le flou uniquement si l'utilisateur n'a pas désactivé les effets de transparence
if (!UIAccessibilityIsReduceTransparencyEnabled()) {
    self.view.backgroundColor = [UIColor clearColor];

    UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark];
    UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
    //remplir toujours la vue
    blurEffectView.frame = self.view.bounds;
    blurEffectView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

    [self.view addSubview:blurEffectView]; //si vous avez plus de UIViews, utilisez une API insertSubview pour le placer où nécessaire
} else {
    self.view.backgroundColor = [UIColor blackColor];
}

Si vous présentez ce view controller de manière modale pour flouter le contenu sous-jacent, vous devrez définir le style de présentation modale sur Over Current Context et définir la couleur d'arrière-plan sur transparent pour garantir que le view controller sous-jacent restera visible une fois qu'il est présenté par-dessus.

0 votes

Génial! @Joey, puis-je appliquer ceci à Sprite Kit? Disons lorsque le jeu est en pause.

9 votes

En tant que clarification du commentaire insertSubView:belowSubView: dans ce code, j'ai utilisé ce qui suit pour définir le flou comme arrière-plan de la vue : view.insertSubview(blurEffectView, atIndex: 0)

0 votes

@Joey est-il possible de sous-classer UIVisualEffectView et ensuite simplement le traiter de manière similaire à une UIView?

289voto

Jano Points 37593

Core Image

Depuis cette image dans la capture d'écran est statique, vous pouvez utiliser CIGaussianBlur de Core Image (nécessite iOS 6). Voici un exemple: https://github.com/evanwdavis/Fun-with-Masks/blob/master/Fun%20with%20Masks/EWDBlurExampleVC.m

Rappelez-vous, c'est plus lent que les autres options disponibles sur cette page.

#import <QuartzCore/QuartzCore.h>

- (UIImage*) blur:(UIImage*)theImage
{   
    // ***********If you need re-orienting (e.g. trying to blur a photo taken from the device camera front facing camera in portrait mode)
    // theImage = [self reOrientIfNeeded:theImage];

    // create our blurred image
    CIContext *context = [CIContext contextWithOptions:nil];
    CIImage *inputImage = [CIImage imageWithCGImage:theImage.CGImage];

    // setting up Gaussian Blur (we could use one of many filters offered by Core Image)
    CIFilter *filter = [CIFilter filterWithName:@"CIGaussianBlur"];
    [filter setValue:inputImage forKey:kCIInputImageKey];
    [filter setValue:[NSNumber numberWithFloat:15.0f] forKey:@"inputRadius"];
    CIImage *result = [filter valueForKey:kCIOutputImageKey];

    // CIGaussianBlur has a tendency to shrink the image a little, 
    // this ensures it matches up exactly to the bounds of our original image
    CGImageRef cgImage = [context createCGImage:result fromRect:[inputImage extent]];

    UIImage *returnImage = [UIImage imageWithCGImage:cgImage];//create a UIImage for this function to "return" so that ARC can manage the memory of the blur... ARC can't manage CGImageRefs so we need to release it before this function "returns" and ends.
    CGImageRelease(cgImage);//release CGImageRef because ARC doesn't manage this on its own.

    return returnImage;

    // *************** if you need scaling
    // return [[self class] scaleIfNeeded:cgImage];
}

+(UIImage*) scaleIfNeeded:(CGImageRef)cgimg {
    bool isRetina = [[[UIDevice currentDevice] systemVersion] intValue] >= 4 && [[UIScreen mainScreen] scale] == 2.0;
    if (isRetina) {
        return [UIImage imageWithCGImage:cgimg scale:2.0 orientation:UIImageOrientationUp];
    } else {
        return [UIImage imageWithCGImage:cgimg];
    }
}

- (UIImage*) reOrientIfNeeded:(UIImage*)theImage{

    if (theImage.imageOrientation != UIImageOrientationUp) {

        CGAffineTransform reOrient = CGAffineTransformIdentity;
        switch (theImage.imageOrientation) {
            case UIImageOrientationDown:
            case UIImageOrientationDownMirrored:
                reOrient = CGAffineTransformTranslate(reOrient, theImage.size.width, theImage.size.height);
                reOrient = CGAffineTransformRotate(reOrient, M_PI);
                break;
            case UIImageOrientationLeft:
            case UIImageOrientationLeftMirrored:
                reOrient = CGAffineTransformTranslate(reOrient, theImage.size.width, 0);
                reOrient = CGAffineTransformRotate(reOrient, M_PI_2);
                break;
            case UIImageOrientationRight:
            case UIImageOrientationRightMirrored:
                reOrient = CGAffineTransformTranslate(reOrient, 0, theImage.size.height);
                reOrient = CGAffineTransformRotate(reOrient, -M_PI_2);
                break;
            case UIImageOrientationUp:
            case UIImageOrientationUpMirrored:
                break;
        }

        switch (theImage.imageOrientation) {
            case UIImageOrientationUpMirrored:
            case UIImageOrientationDownMirrored:
                reOrient = CGAffineTransformTranslate(reOrient, theImage.size.width, 0);
                reOrient = CGAffineTransformScale(reOrient, -1, 1);
                break;
            case UIImageOrientationLeftMirrored:
            case UIImageOrientationRightMirrored:
                reOrient = CGAffineTransformTranslate(reOrient, theImage.size.height, 0);
                reOrient = CGAffineTransformScale(reOrient, -1, 1);
                break;
            case UIImageOrientationUp:
            case UIImageOrientationDown:
            case UIImageOrientationLeft:
            case UIImageOrientationRight:
                break;
        }

        CGContextRef myContext = CGBitmapContextCreate(NULL, theImage.size.width, theImage.size.height, CGImageGetBitsPerComponent(theImage.CGImage), 0, CGImageGetColorSpace(theImage.CGImage), CGImageGetBitmapInfo(theImage.CGImage));

        CGContextConcatCTM(myContext, reOrient);

        switch (theImage.imageOrientation) {
            case UIImageOrientationLeft:
            case UIImageOrientationLeftMirrored:
            case UIImageOrientationRight:
            case UIImageOrientationRightMirrored:
                CGContextDrawImage(myContext, CGRectMake(0,0,theImage.size.height,theImage.size.width), theImage.CGImage);
                break;

            default:
                CGContextDrawImage(myContext, CGRectMake(0,0,theImage.size.width,theImage.size.height), theImage.CGImage);
                break;
        }

        CGImageRef CGImg = CGBitmapContextCreateImage(myContext);
        theImage = [UIImage imageWithCGImage:CGImg];

        CGImageRelease(CGImg);
        CGContextRelease(myContext);
    }

    return theImage;
}

Pile flou (Boîte + Gaussien)

  • StackBlur Cela met en œuvre un mélange de Boîte et de flou Gaussien. 7x plus rapide que les non accéléré gaussien, mais pas si laid que zone de flou. Voir une démo ici (Java version plug-in) ou ici (version JavaScript). Cet algorithme est utilisé dans KDE et Caméra de recul+ et les autres. Il n'utilise pas l'Accélérer Cadre, mais il est rapide.

Accélérer Le Cadre

  • Dans la session "la mise en Œuvre de Mobiliser de l'INTERFACE utilisateur sur iOS" à partir de la WWDC 2013 d'Apple explique comment créer un arrière-plan flou (14:30), et mentionne une méthode applyLightEffect mis en œuvre dans l'exemple de code à l'aide d'Accélérer.cadre.

  • GPUImage utilise OpenGL shaders pour créer des dynamiques de flou. Il dispose de plusieurs types de flou: GPUImageBoxBlurFilter, GPUImageFastBlurFilter, GaussianSelectiveBlur, GPUImageGaussianBlurFilter. Il y a même un GPUImageiOSBlurFilter que "doit parfaitement reproduire l'effet de flou fournis par iOS 7 panneau de commande" (tweet, de l'article). L'article est détaillé et instructif.

 -(UIImage *)blurryGPUImage:(UIImage *)image withBlurLevel:(NSInteger)flou {
 GPUImageFastBlurFilter *blurFilter = [GPUImageFastBlurFilter nouveau];
 blurFilter.blurSize = flou;
 UIImage *résultat = [blurFilter imageByFilteringImage:image];
 résultat de retour;
}

D'autres choses

Andy Matuschak a déclaré sur Twitter: "vous savez, beaucoup de l'endroit où il semble que nous allons le faire en temps réel, il est statique avec des trucs astucieux."

Au doubleencore.com ils disent: "nous avons constaté que 10 pt rayon de flou plus 10 pt augmentation de la saturation imite mieux iOS 7 de l'effet de flou dans la plupart des circonstances".

Un coup d'oeil à la privée les en-têtes de la Pomme de SBFProceduralWallpaperView.

Enfin, ce n'est pas un vrai flou, mais n'oubliez pas que vous pouvez définir rasterizationScale pour obtenir une image pixélisée: http://www.dimzzy.com/blog/2010/11/blur-effect-for-uiview/

0 votes

Merci pour la réponse! Un problème est résolu. Mais nous avons un autre problème. Comment obtenir une image de couverture dans iOS 7. Est-ce possible ?

0 votes

Si vous voulez savoir comment obtenir l'image d'arrière-plan de votre téléphone, je n'ai aucune idée pour le moment. Je n'ai pas vu cette fonctionnalité dans les différences d'API. Peut-être utilise-t-il une API privée.

0 votes

Une chose que j'ai remarquée (et je pourrais me tromper complètement) est que le flou d'Apple semble également ajouter un peu de saturation des couleurs. Donc, je ne pense pas que ce soit un simple flou gaussien.

14voto

xtravar Points 415

Je ne pense pas que je suis autorisé à publier le code, mais le post ci-dessus mentionnant le code d'exemple de la WWDC est correct. Voici le lien: https://developer.apple.com/downloads/index.action?name=WWDC%202013

Le fichier que vous cherchez est la catégorie sur UIImage, et la méthode est applyLightEffect.

Comme je l'ai noté ci-dessus dans un commentaire, le flou d'Apple a une saturation et d'autres éléments en plus du flou. Un simple flou ne suffira pas... si vous cherchez à imiter leur style.

8 votes

Ce lien est cassé. Voici le lien correct: developer.apple.com/downloads/index.action?name=WWDC%202013

0 votes

Notez que ce code exemple nécessite XCode 5.0 et iOS SDK 7.0 (qui n'ont pas encore été publiés publiquement)

0 votes

Merci pour le lien corrigé, cependant il y a quelques extraits de code dedans, lequel contient la catégorie UIImage pertinente ?

9voto

Sam Points 1625

Je pense que la solution la plus simple à cela est de remplacer UIToolbar, qui floute tout ce qui se trouve derrière lui dans iOS 7. C'est assez sournois, mais c'est très simple à implémenter et rapide!

Vous pouvez le faire avec n'importe quelle vue, il suffit d'en faire une sous-classe de UIToolbar au lieu de UIView. Vous pouvez même le faire avec la propriété view d'un UIViewController, par exemple...

1) Créez une nouvelle classe qui est une "Sous-classe de" UIViewController et cochez la case "Avec XIB pour interface utilisateur".

2) Sélectionnez la vue et allez à l'inspecteur d'identité dans le panneau de droite (alt-commande-3). Changez la "Classe" en UIToolbar. Maintenant allez à l'inspecteur d'attributs (alt-commande-4) et changez la couleur "Fond" en "Couleur transparente".

3) Ajoutez une sous-vue à la vue principale et reliez-la à un IBOutlet dans votre interface. Appelez-la backgroundColorView. Cela ressemblera à ceci, en tant que catégorie privée dans le fichier d'implémentation (.m).

@interface BlurExampleViewController ()
@property (weak, nonatomic) IBOutlet UIView *backgroundColorView;
@end

4) Allez dans le fichier d'implémentation du contrôleur de vue (.m) et modifiez la méthode -viewDidLoad, de la manière suivante :

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.view.barStyle = UIBarStyleBlack; // cela donnera un flou noir comme dans le message original
    self.backgroundColorView.opaque = NO;
    self.backgroundColorView.alpha = 0.5;
    self.backgroundColorView.backgroundColor = [UIColor colorWithWhite:0.3 alpha:1];
}

Cela vous donnera une vue gris foncé, qui floute tout ce qui se trouve derrière. Pas de bizarreries, pas de ralentissement du flou d'image de base, en utilisant tout ce que vous avez à portée de main fourni par le système d'exploitation/SDK.

Vous pouvez ajouter la vue de ce contrôleur de vue à une autre vue, comme suit :

[self addChildViewController:self.blurViewController];
[self.view addSubview:self.blurViewController.view];
[self.blurViewController didMoveToParentViewController:self];

// animer le self.blurViewController dans la vue

Dites-moi si quelque chose n'est pas clair, je serai heureux de vous aider!


Modifier

UIToolbar a été modifié en 7.0.3 pour donner un effet potentiellement indésirable lors de l'utilisation d'un flou coloré.

Nous pouvions auparavant définir la couleur en utilisant barTintColor, mais si vous le faisiez avant, vous devrez définir le composant alpha à moins de 1. Sinon, votre UIToolbar aura une couleur complètement opaque - sans aucun flou.

Cela peut être réalisé comme suit : (en gardant à l'esprit que self est une sous-classe de UIToolbar)

UIColor *color = [UIColor blueColor]; // par exemple
self.barTintColor = [color colorWithAlphaComponent:0.5];

Cela donnera une teinte bleuâtre à la vue floue.

1 votes

Pas mal mec. J'ai utilisé ces trois lignes dans ma vue : self.backgroundColorView.opaque = NO; self.backgroundColorView.alpha = 0.5; self.backgroundColorView.backgroundColor = [UIColor colorWithWhite:0.3 alpha:1]; Mais l'arrière-plan n'est pas flou, cela crée juste un bel effet de superposition. Merci quand même !

1 votes

Je ne vois aucun flou du tout en utilisant cette technique. Cela crée simplement un recouvrement coloré.

0 votes

Assurez-vous que la superposition colorée alpha est inférieure à 1. Vous pouvez utiliser une UIToolbar sans le contrôleur de vue, ce qui peut être plus simple en fonction de vos besoins.

1voto

not really Jake Points 3440

Je l'ai trouvé par accident, cela me donne vraiment d'excellents résultats (presque identiques à ceux d'Apple) et utilise le framework d'Acceleration. -- http://pastebin.com/6cs6hsyQ *Non écrit par moi

8 votes

C'est en fait du code Apple provenant de la WWDC 2013 avec un copyright incorrect.

0 votes

Les codes du WWDC ne sont-ils pas protégés par le droit d'auteur, et l'accès est-il uniquement autorisé aux membres ayant des abonnements payants ?

1 votes

Peut-être, mais le code ci-dessus que j'ai trouvé en utilisant Google. Je n'ai pas modifié le Copyright et j'ai supposé (et je suppose toujours) qu'il a la revendication de copyright correcte. Si Apple est en désaccord, ils devraient mettre leurs efforts pour le supprimer. Je ne vois pas la pertinence.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X