Estoy tratando de replicar este fondo borroso de la pantalla de ejemplo de iOS 7 lanzada públicamente de Apple:
Esta pregunta sugiere aplicar un filtro CI al contenido a continuación, pero ese es un enfoque completamente diferente. Es obvio que iOS 7 no captura el contenido de las vistas a continuación, por muchas razones:
- Hacer algunas pruebas aproximadas, capturar una captura de pantalla de las vistas a continuación y aplicar un filtro CIGaussianBlur con un radio lo suficientemente grande para imitar el estilo de desenfoque de iOS 7 toma 1-2 segundos, incluso en un simulador.
- La vista borrosa de iOS 7 puede difuminarse sobre vistas dinámicas, como un video o animaciones, sin un retraso notable.
¿Alguien puede formular una hipótesis sobre qué marcos podrían estar usando para crear este efecto y si es posible crear un efecto similar con las API públicas actuales?
Editar: (del comentario) No sabemos exactamente cómo lo está haciendo Apple, pero ¿hay alguna suposición básica que podamos hacer? Podemos suponer que están usando hardware, ¿verdad?
¿El efecto es autónomo en cada vista, de modo que el efecto no sabe realmente qué hay detrás? ¿O, en función de cómo funcionan los desenfoques, se debe tener en cuenta el contenido detrás del desenfoque?
Si los contenidos detrás del efecto son relevantes, ¿podemos suponer que Apple está recibiendo un "feed" de los contenidos a continuación y los procesa continuamente con un desenfoque?
fuente
Respuestas:
¿Por qué molestarse en replicar el efecto? Simplemente dibuje una barra UIToolbar detrás de su vista.
myView.backgroundColor = [UIColor clearColor]; UIToolbar* bgToolbar = [[UIToolbar alloc] initWithFrame:myView.frame]; bgToolbar.barStyle = UIBarStyleDefault; [myView.superview insertSubview:bgToolbar belowSubview:myView];
fuente
frame
otransform
de esta barra de herramientas / vista o algo así, o sucederán cosas malas. ¡También sugirió encarecidamente presentar informes de errores de radar sobre esto, para construir un caso internamente para que podamos obtener una API pública real para este efecto!UITabBar
mismo.Apple lanzó código en WWDC como una categoría en UIImage que incluye esta funcionalidad, si tiene una cuenta de desarrollador, puede obtener la categoría UIImage (y el resto del código de muestra) yendo a este enlace: https://developer.apple. com / wwdc / schedule / y busque la sección 226 y haga clic en los detalles. No he jugado con él todavía, pero creo que el efecto será mucho más lento en iOS 6, hay algunas mejoras en iOS 7 que hacen que capturar la captura de pantalla inicial que se usa como entrada para el desenfoque sea mucho más rápido.
Enlace directo: https://developer.apple.com/downloads/download.action?path=wwdc_2013/wwdc_2013_sample_code/ios_uiimageeffects.zip
fuente
En realidad, apuesto a que esto sería bastante sencillo de lograr. Probablemente no funcionaría ni se vería exactamente como lo que está pasando Apple, pero podría estar muy cerca.
En primer lugar, debe determinar el CGRect de la UIView que presentará. Una vez que haya determinado que solo necesitará tomar una imagen de la parte de la interfaz de usuario para que pueda desenfocarse. Algo como esto...
- (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; }
Desenfoque gaussiano: recomendado
Usando la
UIImage+ImageEffects
categoría que Apple proporciona aquí , obtendrá un desenfoque gaussiano que se parece mucho al desenfoque en iOS 7.Caja de desenfoque
También puede usar un cuadro de desenfoque usando la siguiente
boxBlurImageWithBlur:
categoría de UIImage. Esto se basa en un algoritmo que puede encontrar aquí .@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
Ahora que está calculando el área de la pantalla para desenfocar, pasándola a la categoría de desenfoque y recibiendo una imagen de UII que se ha desenfocado, ahora todo lo que queda es establecer esa imagen desenfocada como fondo de la vista que presentará. Como dije, esto no será una combinación perfecta para lo que está haciendo Apple, pero aún así debería verse bastante bien.
Espero eso ayude.
fuente
renderInContext
, use el nuevodrawViewHierarchyInRect:
osnapshotView:
. La charla 216 de la WWDC "Implementación de una interfaz de usuario atractiva en iOS7" afirma una mejora del rendimiento de 5-15 veces.iOS8 respondió estas preguntas.
- (instancetype)initWithEffect:(UIVisualEffect *)effect
o Swift:
init(effect effect: UIVisualEffect)
fuente
Acabo de escribir mi pequeña subclase de UIView que tiene la capacidad de producir desenfoque nativo de iOS 7 en cualquier vista personalizada. Utiliza UIToolbar pero de una manera segura para cambiar su marco, límites, color y alfa con animación en tiempo real.
Avíseme si nota algún problema.
https://github.com/ivoleko/ILTranslucentView
fuente
Existe un rumor de que los ingenieros de Apple afirmaron que para hacer este rendimiento están leyendo directamente del búfer de la gpu, lo que plantea problemas de seguridad, por lo que todavía no hay una API pública para hacer esto.
fuente
Esta es una solución que puedes ver en los videos de la WWDC. Tienes que hacer un Desenfoque Gaussiano, así que lo primero que tienes que hacer es agregar un nuevo archivo .my .h con el código que estoy escribiendo aquí, luego tienes que hacer una captura de pantalla, usar el efecto deseado y agréguelo a su vista, luego su UITable UIView o lo que sea que tenga que ser transparente, puede jugar con applyBlurWithRadius, para archivar el efecto deseado, esta llamada funciona con cualquier UIImage.
Al final, la imagen borrosa será el fondo y el resto de los controles de arriba tienen que ser transparentes.
Para que esto funcione, debe agregar las siguientes bibliotecas:
Acelerate.framework, UIKit.framework, CoreGraphics.framework
Espero que te guste.
Codificación feliz.
//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; }
fuente
Puede encontrar su solución en la DEMO de Apple en esta página: WWDC 2013 , descubra y descargue el código de muestra de UIImageEffects.
Luego con el código de @Jeremy Fox. Lo cambié a
- (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]; }
Espero que esto te ayudará.
fuente
Aquí hay una forma realmente fácil de hacerlo: https://github.com/JagCesar/iOS-blur
Simplemente copie la capa de UIToolbar y listo, AMBlurView lo hace por usted. De acuerdo, no es tan borroso como el centro de control, pero es lo suficientemente borroso.
Recuerde que iOS7 está bajo NDA.fuente
Cada respuesta aquí usa vImageBoxConvolve_ARGB8888.Esta función es muy, muy lenta, eso está bien, si el rendimiento no es un requisito de alta prioridad, pero si está utilizando esto para la transición entre dos controladores de vista (por ejemplo), este enfoque significa tiempos superiores a 1 segundo o quizás más, eso es muy malo para la experiencia del usuario de su aplicación.
Si prefiere dejar todo este procesamiento de imágenes a la GPU (y debería) puede obtener un efecto mucho mejor y también tiempos increíbles redondeando 50ms (suponiendo que tenga un tiempo de 1 segundo en el primer enfoque), entonces, hagámoslo. .
Primero descargue GPUImage Framework (con licencia BSD) aquí .
A continuación, agregue las siguientes clases (.my .h) de GPUImage (no estoy seguro de que sean el mínimo necesario solo para el efecto de desenfoque)
GPUImagenDosPasadaTexturaMuestreoFiltro
iOS / GPUImage-Prefix.pch
A continuación, cree una categoría en UIImage, que agregará un efecto de desenfoque a un UIImage existente:
#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
Y por último, agregue los siguientes marcos a su proyecto:
AVFoundation CoreMedia CoreVideo OpenGLES
Sí, me divertí con este enfoque mucho más rápido;)
fuente
Puede intentar usar mi vista personalizada, que tiene la capacidad de desenfocar el fondo. Lo hace simulando tomar una instantánea del fondo y desenfocarlo, como en el código WWDC de Apple. Es muy facíl de usar.
También hice algunas mejoras para simular el desenfoque dinámico sin perder el rendimiento. El fondo de mi vista es un scrollView que se desplaza con la vista, por lo que proporciona el efecto de desenfoque para el resto de la supervista.
Vea el ejemplo y el código en mi GitHub
fuente
Core Background implementa el efecto deseado de iOS 7.
https://github.com/justinmfischer/core-background
Descargo de responsabilidad: soy el autor de este proyecto
fuente