[英]How can I produce an effect similar to the iOS 7 blur view?
我正在尝试从Apple公开发布的iOS 7示例屏幕中复制这种模糊的背景:
这个问题建议对下面的内容应用CI过滤器,但这是一种完全不同的方法。 显然,由于多种原因,iOS 7并未捕获以下视图的内容:
谁能假设他们可以使用什么框架来产生这种效果,以及是否有可能使用当前的公共API来产生类似的效果?
编辑:(从评论中)我们不完全知道苹果的做法,但是我们可以做一些基本的假设吗? 我们可以假设他们正在使用硬件,对吗?
效果是否在每个视图中都是独立的,从而使效果实际上不知道其背后是什么? 还是必须根据模糊的工作原理来考虑模糊背后的内容?
如果效果背后的内容相关,我们是否可以假设Apple正在接收下面内容的“提要”,并不断对其进行模糊处理?
为什么要麻烦复制效果? 只需在视图后面绘制一个UIToolbar。
myView.backgroundColor = [UIColor clearColor];
UIToolbar* bgToolbar = [[UIToolbar alloc] initWithFrame:myView.frame];
bgToolbar.barStyle = UIBarStyleDefault;
[myView.superview insertSubview:bgToolbar belowSubview:myView];
Apple在WWDC上发布了包含此功能的UIImage类别中的代码,如果您拥有开发人员帐户,则可以通过转到以下链接获取UIImage类别(以及示例代码的其余部分): https : //developer.apple。 com / wwdc / schedule /,然后浏览第226节并单击详细信息。 我还没有玩过它,但是我认为在iOS 6上效果会慢很多,iOS 7进行了一些增强,可以更快地捕获用作模糊输入的初始屏幕截图。
直接链接: https : //developer.apple.com/downloads/download.action?path= wwdc_2013/wwdc_2013_sample_code/ ios_uiimageeffects.zip
实际上,我敢打赌,这将很容易实现。 它可能无法运行或看起来完全不像苹果公司正在发生的事情,但可能非常接近。
首先,您需要确定将要呈现的UIView的CGRect。 一旦确定了您只需要获取UI部分的图像即可使其模糊。 像这样
- (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;
}
使用这里提供的UIImage+ImageEffects
类别Apple,您将获得高斯模糊,看起来非常像iOS 7中的模糊。
您还可以使用以下boxBlurImageWithBlur:
使用框模糊boxBlurImageWithBlur:
UIImage类别。 这基于您可以在此处找到的算法。
@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
现在您正在计算要模糊的屏幕区域,将其传递到模糊类别,并收到已模糊的UIImage,现在剩下的就是将模糊的图像设置为将要呈现的视图的背景。 就像我说的那样,这并不是苹果所做的完美选择,但它看起来仍然很酷。
希望能帮助到你。
iOS8回答了这些问题。
- (instancetype)initWithEffect:(UIVisualEffect *)effect
或Swift:
init(effect effect: UIVisualEffect)
我刚刚编写了UIView的小子类,它具有在任何自定义视图上生成本机iOS 7模糊的能力。 它使用UIToolbar,但以一种安全的方式通过实时动画更改其帧,边界,颜色和Alpha。
如果您发现任何问题,请告诉我。
有传言称,Apple工程师声称,要使这种性能直接从gpu缓冲区中读取,就会引发安全问题,这就是为什么现在还没有公共API可以这样做的原因。
这是一种非常简单的方法: https : //github.com/JagCesar/iOS-blur
只需复制UIToolbar的图层即可完成,AMBlurView会为您完成。 好的,它不像控制中心那样模糊,但是足够模糊。
请记住,iOS7在NDA下。
您可以在以下页面的Apple演示中找到您的解决方案: WWDC 2013 ,查找并下载UIImageEffects示例代码。
然后使用@Jeremy Fox的代码。 我将其更改为
- (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];
}
希望这会帮助你。
您可以在WWDC的视频中看到此解决方案。 您必须执行高斯模糊,所以首先要做的是使用我在此处编写的代码添加一个新的.m和.h文件,然后您必须进行制作和屏幕截图,使用所需的效果并将其添加到视图中,然后将UITable UIView或必须透明的对象添加到视图中,则可以使用applyBlurWithRadius来播放所需的效果,此调用可与任何UIImage一起使用。
最后,模糊的图像将成为背景,并且上面的其余控件必须透明。
为此,您必须添加以下库:
Acelerate框架,UIKit框架,CoreGraphics框架
我希望你喜欢它。
快乐的编码。
//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;
}
此处的每个响应都使用vImageBoxConvolve_ARGB8888 ,如果性能不是高优先级要求,则此函数的确非常慢,这很好,但是,如果您使用它在两个View Controller之间进行转换(例如),则此方法意味着超过1倍第二甚至更多,这对您的应用程序的用户体验非常不利。
如果您希望将所有图像处理都留给GPU(并且应该这样做),则可以得到更好的效果,并且还可以舍入50ms的绝佳时间(假设第一种方法的时间为1秒),因此,让我们来做吧。
首先下载GPUImage框架(BSD许可) 在这里 。
接下来,从GPUImage中添加以下类(.m和.h)(我不确定这些仅是模糊效果所需的最小值)
GPUImageTwoPassTextureSamplingFilter
iOS / GPUImage-Prefix.pch
接下来,在UIImage上创建一个类别,这将为现有的UIImage添加模糊效果:
#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
最后,将以下框架添加到您的项目中:
AVFoundation CoreMedia CoreVideo OpenGLES
是的,这种更快的方法很有趣;)
您可以尝试使用我的自定义视图,该视图具有模糊背景的功能。 它通过伪造背景快照并对其进行模糊处理来实现此目的,就像苹果的WWDC代码中的快照一样。 使用非常简单。
我还做了一些改进,以伪造动态模糊而不损失性能。 我视图的背景是scrollView,它随视图滚动,因此为其余的Superview提供了模糊效果。
在我的GitHub上查看示例和代码
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.