Comment puis-je animer un effet de flou gaussien dans iOS?

pour l'ensemble de la sensation iOS 7, je veux appliquer un effet flou à une partie spécifique de l'écran pour l'obscurcir, mais je ne veux pas simplement jeter le flou instantanément, je veux l'animer et l'animer de sorte que l'utilisateur voit presque l'effet Flou appliqué.

presque comme dans Photoshop vous avez changé la valeur de flou gaussien petit à petit de 0 à 10, au lieu de 0 à 10 en une seule fois.

j'ai essayé quelques solutions à cela, la suggestion la plus populaire étant de il suffit de placer la vue floue sur une vue non floue, puis d'abaisser la valeur alpha de la vue floue.

Cela fonctionne ok, mais pas très agréable à l'oeil car il n'y a pas de transition, c'est juste un superposition. Exemple:

enter image description here

Quelle serait la meilleure façon d'atteindre un tel effet? Je suis familier avec GPUImage, mais vous ne savez pas comment l'accomplir.

Ce serait génial si je pouvais contrôler quel pourcentage de flou il est, de sorte qu'il pourrait être appliqué interactivement de l'utilisateur. (par exemple: l'utilisateur traîne à mi-chemin, le flou est appliqué à moitié, etc.)

24
demandé sur Leo Natan 2014-02-26 08:02:05

5 réponses

avec iOS 9 et plus, vous pouvez animer l'effet sur les vues d'effet visuel:

[UIView animateWithDuration:0.3 animations: ^ {
    visualEffectView.effect = blurEffect;
} completion:nil];

Cela permettra d'atteindre l'effet escompté de l'animation du rayon de flou.


je pense que si vous animez un fondu enchaîné de non-troubles de la vue -> vue floue, il sera assez agréable. Vous devez animer l'affichage de la vue flou sur le dessus de la vue non flou.

supposons blurView est la vue contenant le flou effet.

voici deux exemples de la façon d'accomplir une transition animée:

[UIView transitionWithView:self.view duration:0.3 options:UIViewAnimationOptionTransitionCrossDissolve animations: ^ {
    [self.view addSubview:blurView];
} completion:nil];

Ici je suppose blurView a déjà été configuré, et il suffit de faire la transition de l'ajout comme subview dans une animation.

Vous pouvez également obtenir d'une autre façon:

blurView.alpha = 0.0f;
[UIView animateWithDuration:0.3 animations: ^ {
    blurView.alpha = 1.0f;
} completion:nil];

ici, je rends le flou transparent et j'Anime son apparence. Notez que cette méthode ne fonctionnera pas avec hidden, donc vous voulez utiliser le alpha propriété.


si vous souhaitez contrôler le montant du flou de façon interactive, voici un exemple sur la façon d'atteindre:

tout d'abord, créez une image de la vue que vous voulez flouter:

UIGraphicsBeginImageContextWithOptions(nonBlurredView.bounds.size, NO, self.view.window.screen.scale);
[nonBlurredView drawViewHierarchyInRect:nonBlurredView.bounds afterScreenUpdates:NO];
UIImage *snapshotImage = UIGraphicsGetImageFromCurrentImageContext();

Gardez cette image. Vous ne voulez redessiner de la même façon que si la vue a été changée. Sinon, vous devriez réutiliser cette image, car il est coûteux de dessiner la hiérarchie.

Maintenant, quand vous avez besoin de flou, utilisez la commande suivante code:

CIImage *imageToBlur = [CIImage imageWithCGImage:snapshotImage.CGImage];    
CIFilter *gaussianBlurFilter = [CIFilter filterWithName: @"CIGaussianBlur"]; 
[gaussianBlurFilter setValue:imageToBlur forKey: @"inputImage"]; 
[gaussianBlurFilter setValue:@10 forKey: @"inputRadius"]; //Here you input the blur radius - the larger, the 
CIImage *resultImage = [gaussianBlurFilter valueForKey: @"outputImage"]; 
UIImage *endImage = [[UIImage alloc] initWithCIImage:resultImage];

inputRadius entrée donne définit la quantité de flou effectuée. Si vous l'animez, vous obtiendrez une sensation interactive.

mais je pense toujours que l'ancienne méthode est beaucoup plus facile et se sentira aussi bien pour l'utilisateur.

41
répondu Leo Natan 2016-10-03 09:12:17

c'est facile sur OS X, où une vue peut CIFilter un effet sur la vue derrière elle, et où le processeur est puissant et rapide. Sur iOS, cependant, il n'y a pas de filtres de composition de vue, et le flou prend du temps: on ne peut pas le faire à plusieurs reprises, en direct, aussi vite que les cadres de l'animation. Il est donc nécessaire de trouver une forme de compromis.

Si vous connaissez suffisamment à l'avance ce que vous voulez de flou, cependant, vous pouvez préparer une série d'images à l'aide d'un dégradé de le flou: pas de flou, un peu flou, un peu plus flou, et ainsi de suite. Ensuite, vous assemblez les images et les "jouez" comme les "cadres" d'un UIImageView animé.

Qui, cependant, n'est pas ce que je voudrais conseiller. Ce que je ferais est de préparer l'image floue et l'image non floue et d'utiliser une CIDissolveTransition pour se dissoudre de l'image non floue à l'image floue. Non, ce n'est pas la même chose que l'application progressive du flou, mais c'est computationnellement peu coûteux et est le meilleur "dissoudre" l'effet dans la boîte à outils.

6
répondu matt 2014-04-06 15:32:36

je suggère rarement des solutions standard pour les problèmes de codage. Toutefois, dans ce cas, je recommande vivement LiveFrost. C'est une sous-classe D'UIView vraiment solide qui se concentre exclusivement sur la réplique du populaire effet de flou gaussien iOS 7.

je vous encourage également à lire le blog de l'auteur concernant ses décisions de design pour cette classe. J'ai fait beaucoup de recherche spécifiquement sur ce sujet depuis la sortie de iOS 7, et j'ai littéralement zéro Plaintes au sujet de ce code.

il a même flou animation de la boîte! Bonne chance à vous :)

3
répondu Sam 2014-04-07 15:09:05

j'ai développé un petit projet qui utilise GPUImage pour le flou vivant avec un rayon de flou variable et un framerate ( MSLiveBlur) - cela ressemble exactement ce dont vous avez besoin.

dans l'application échantillon j'ai un curseur qui augmente / diminue le niveau de flou en réponse à l'action de l'utilisateur, comme vous l'avez mentionné dans votre question. Si vous voulez l'animer sans interaction utilisateur, vous pouvez faire un minuteur qui augmente lentement le rayon de flou jusqu'à ce que vous atteigniez la valeur finale. Vous Je ne pourrais pas utiliser CoreAnimation avec cette solution.

[[MSLiveBlurView sharedInstance] setBlurInterval:0.2];
[[MSLiveBlurView sharedInstance] blurRect:someView.frame];

// Count x from 0 to your final radius
[MSLiveBlurView sharedInstance].blurRadius = x;
3
répondu michaels 2014-04-11 17:51:57

Voici une fonction que vous pouvez utiliser pour animer un flou avec des images précalculées. Vous dites que vous êtes familier avec GPUImage donc je l'ai utilisé (mais il peut fonctionner aussi avec un algorithme de flou simple).

avec ceci vous pouvez animer véritable floutemps réel avec un coût de 1-2% CPU

// configuration
let imageCount: Int = 10
let blurMax: Float = 40.0
// keep images in memory
var currentIdx: Int = 0
var imgs: [UIImage] = []

// func that create the precomputed images with GPUImage (call it in the viewDidLoad for example)
func initBlur() {
    let blur = GaussianBlur()
    let myImage: UIImage = UIImage(named: "your_image")!
    let pictureInput = PictureInput(image: myImage)
    let pictureOutput = PictureOutput()
    pictureOutput.onlyCaptureNextFrame = false

    pictureOutput.imageAvailableCallback = { image in
        self.imgs.append(image)
    }

    pictureInput --> blur --> pictureOutput

    for i in 0...imageCount {
        blur.blurRadiusInPixels = (Float(i) / Float(imageCount)) * blurMax
        pictureInput.processImage(synchronously: true)
    }
}

// function that return the correct image from the image set with a blur percentage from a value between 0 and 1 where 0 = no blur and 1 full blurred.
func getBlurredImage(value: Float) -> UIImage? {
    // return nil if there isn't precompiled images
    if imgs.count == 0 {
        return nil
    }

    // get the desired blurred image index
    let idx = Int(value * Float(imageCount - 1))
    // if the index changed, check the correctness of the index
    if currentIdx != idx {
        if idx < 0 {
            currentIdx = 0
        }
        else if idx >= imgs.count {
            currentIdx = imageCount - 1
        }
        else {
            currentIdx = idx
        }
    }
    return imgs[currentIdx]
}

Et c'est tout, ensuite vous pouvez l'utiliser par exemple avec une minuterie ou dans un scrollViewDidScroll fonction de la forme:

imageView.image = getBlurredImage(value: yOffset / height)
1
répondu Anthony 2017-02-27 10:54:30