sacar - ¿Cómo animo dentro/fuera un efecto de desenfoque gaussiano en iOS?
desenfoque photoshop (5)
Para toda la sensación de iOS 7, quiero aplicar un efecto de desenfoque a una parte específica de la pantalla para ofuscarlo, pero no quiero simplemente lanzar el desenfoque al instante, quiero animarlo y animarlo de manera que el usuario casi ve el efecto de desenfoque aplicado.
Casi como si en Photoshop cambiase el valor de desenfoque gaussiano poco a poco de 0 a 10, en lugar de 0 a 10 de una vez.
He intentado algunas soluciones para esto, la sugerencia más popular es simplemente poner la vista borrosa encima de una vista no borrosa y luego reducir el valor alfa de la vista borrosa.
Esto funciona bien , pero no muy agradable a la vista, ya que no hay transición, es solo una superposición. Ejemplo:
¿Cuál sería una mejor manera de lograr tal efecto? Estoy familiarizado con GPUImage , pero no estoy seguro de cómo lograrlo con eso.
También sería genial si pudiera controlar en qué porcentaje del desenfoque se encuentra, por lo que podría aplicarse interactivamente desde el usuario. (por ejemplo: el usuario arrastra hasta la mitad, el desenfoque se aplica a medias, etc.)
Aquí hay una función que puede utilizar para animar un desenfoque con imágenes precalculadas. Dice que está familiarizado con GPUImage, así que lo usé (pero puede funcionar también con un algoritmo de desenfoque simple).
Con esto puede animar el desenfoque real en tiempo real con un costo de 1-2% de 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]
}
Y eso es todo, a continuación, puede usarlo, por ejemplo, con un temporizador o en una función scrollViewDidScroll como:
imageView.image = getBlurredImage(value: yOffset / height)
Con iOS 9 y superior, puedes animar el efecto en las vistas de efectos visuales:
[UIView animateWithDuration:0.3 animations: ^ {
visualEffectView.effect = blurEffect;
} completion:nil];
Esto logrará el efecto esperado de animar el radio de desenfoque.
Creo que si animas un crossfade desde una vista no borrosa -> una vista borrosa, será lo suficientemente agradable. Debe animar la visualización de la vista borrosa encima de la vista no borrosa.
Supongamos que blurView
es la vista que contiene el efecto de desenfoque.
Aquí hay dos ejemplos de cómo lograr una transición animada:
[UIView transitionWithView:self.view duration:0.3 options:UIViewAnimationOptionTransitionCrossDissolve animations: ^ {
[self.view addSubview:blurView];
} completion:nil];
Aquí asumo que blurView
ya se ha configurado, y solo transiciono la adición como subvista en una animación.
También puedes lograr esto de una manera diferente:
blurView.alpha = 0.0f;
[UIView animateWithDuration:0.3 animations: ^ {
blurView.alpha = 1.0f;
} completion:nil];
Aquí, hago la vista borrosa transparente y animo su apariencia. Tenga en cuenta que el uso de este método no funcionará con la hidden
, por lo que desea utilizar la propiedad alpha
.
Si desea controlar la cantidad de desenfoque de manera interactiva, aquí hay un ejemplo de cómo lograrlo:
Primero crea una imagen de la vista que quieras difuminar:
UIGraphicsBeginImageContextWithOptions(nonBlurredView.bounds.size, NO, self.view.window.screen.scale);
[nonBlurredView drawViewHierarchyInRect:nonBlurredView.bounds afterScreenUpdates:NO];
UIImage *snapshotImage = UIGraphicsGetImageFromCurrentImageContext();
Mantener esta imagen Solo desea volver a dibujar de forma similar si se ha cambiado la vista. De lo contrario, debe reutilizar esta imagen, ya que es caro dibujar la jerarquía.
Ahora, cuando necesite difuminar, use el siguiente código:
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];
La entrada inputRadius
proporciona la cantidad de desenfoque realizado. Si animas esto, lograrás una sensación interactiva.
Pero sigo pensando que el método anterior es mucho más fácil y se sentirá tan bien para el usuario.
Esto es fácil en OS X, donde una vista puede CIFilter un efecto en la vista detrás de ella, y donde el procesador es potente y rápido. En iOS, sin embargo, no hay filtros de vista compositiva, y el desenfoque toma algo de tiempo: no se puede hacer repetidamente, en vivo, tan rápido como los cuadros de la animación. Por lo tanto, se necesita alguna forma de compromiso.
Sin embargo, si sabe con suficiente antelación qué es lo que desea difuminar, puede preparar una serie de imágenes con una gradación de desenfoque: sin desenfoque, un poco de desenfoque, un poco más de desenfoque, etc. Luego ensamblas las imágenes y "juegas" como los "marcos" de un UIImageView animado.
Eso, sin embargo, no es lo que realmente aconsejaría. Lo que haría sería preparar la imagen borrosa y la imagen no borrosa y utilizar una Transición CISissolve para disolver de la imagen no borrosa a la borrosa. No, no es lo mismo que aplicar gradualmente el desenfoque, pero es computacionalmente económico y es el mejor efecto de "disolución" en la caja de herramientas.
He desarrollado un pequeño proyecto que utiliza GPUImage para desenfoque en vivo con radio de desenfoque variable y MSLiveBlur cuadros ( MSLiveBlur ): esto suena exactamente como lo que necesitas.
En la aplicación de muestra tengo un control deslizante que aumenta / disminuye el nivel de desenfoque en respuesta a la acción del usuario, como mencionó en su pregunta. Si desea animarlo sin la interacción del usuario, puede crear un temporizador que aumente lentamente el radio de desenfoque hasta alcanzar el valor final. Sin embargo, no podrías usar CoreAnimation con esta solución.
[[MSLiveBlurView sharedInstance] setBlurInterval:0.2];
[[MSLiveBlurView sharedInstance] blurRect:someView.frame];
// Count x from 0 to your final radius
[MSLiveBlurView sharedInstance].blurRadius = x;
Rara vez sugiero soluciones disponibles para problemas de codificación. Sin embargo, en este caso, recomendaría sinceramente LiveFrost . Es una subclase UIView realmente sólida centrada exclusivamente en replicar el popular efecto de desenfoque gaussiano de iOS 7.
También lo alentaría a leer el blog del autor con respecto a sus decisiones de diseño para esta clase. He investigado mucho específicamente en este tema desde el lanzamiento de iOS 7, y tengo literalmente CERO QUEJAS sobre este código.
Incluso tiene desenfoque de animación fuera de la caja! Buena suerte para ti :)