ios 创建模糊叠加视图
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17041669/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Creating a blurring overlay view
提问by kondratyevdev
In the Music app of the new iOS, we can see an album cover behind a view that blurs it.
在新 iOS 的音乐应用程序中,我们可以在模糊的视图后面看到专辑封面。
How can something like that be accomplished? I've read the documentation, but did not find anything there.
怎么可能完成这样的事情?我已经阅读了文档,但在那里没有找到任何东西。
回答by Jordan H
You can use UIVisualEffectView
to achieve this effect. This is a native API that has been fine-tuned for performance and great battery life, plus it's easy to implement.
你可以用它UIVisualEffectView
来达到这个效果。这是一个原生 API,已针对性能和更长的电池寿命进行了微调,而且易于实现。
Swift:
迅速:
//only apply the blur if the user hasn't disabled transparency effects
if !UIAccessibility.isReduceTransparencyEnabled {
view.backgroundColor = .clear
let blurEffect = UIBlurEffect(style: .dark)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
//always fill the view
blurEffectView.frame = self.view.bounds
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(blurEffectView) //if you have more UIViews, use an insertSubview API to place it where needed
} else {
view.backgroundColor = .black
}
Objective-C:
目标-C:
//only apply the blur if the user hasn't disabled transparency effects
if (!UIAccessibilityIsReduceTransparencyEnabled()) {
self.view.backgroundColor = [UIColor clearColor];
UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark];
UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
//always fill the view
blurEffectView.frame = self.view.bounds;
blurEffectView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:blurEffectView]; //if you have more UIViews, use an insertSubview API to place it where needed
} else {
self.view.backgroundColor = [UIColor blackColor];
}
If you are presenting this view controller modally to blur the underlying content, you'll need to set the modal presentation style to Over Current Context and set the background color to clear to ensure the underlying view controller will remain visible once this is presented overtop.
如果您以模态方式呈现此视图控制器以模糊底层内容,则需要将模态呈现样式设置为 Over Current Context 并将背景颜色设置为 clear 以确保底层视图控制器在将其呈现在顶部时保持可见。
回答by Jano
Core Image
核心形象
Since that image in the screenshot is static, you could use CIGaussianBlur
from Core Image (requires iOS 6). Here is sample: https://github.com/evanwdavis/Fun-with-Masks/blob/master/Fun%20with%20Masks/EWDBlurExampleVC.m
由于屏幕截图中的图像是静态的,您可以使用CIGaussianBlur
Core Image(需要 iOS 6)。这是示例:https: //github.com/evanwdavis/Fun-with-Masks/blob/master/Fun%20with%20Masks/EWDBlurExampleVC.m
Mind you, this is slower than the other options on this 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;
}
Stack blur (Box + Gaussian)
堆栈模糊(框 + 高斯)
- StackBlurThis implements a mix of Box and Gaussian blur. 7x faster than non accelerated gaussian, but not so ugly as box blur. See a demo in here(Java plugin version) or here(JavaScript version). This algorithm is used in KDE and Camera+ and others. It doesn't use the Accelerate Framework but it's fast.
- StackBlur这实现了 Box 和 Gaussian 模糊的混合。比非加速高斯快 7 倍,但不像框模糊那么难看。在此处(Java 插件版本)或此处(JavaScript 版本)查看演示。该算法用于 KDE 和 Camera+ 等。它不使用加速框架,但速度很快。
Accelerate Framework
加速框架
In the session “Implementing Engaging UI on iOS” from WWDC 2013Apple explains how to create a blurred background (at 14:30), and mentions a method
applyLightEffect
implemented in the sample code using Accelerate.framework.GPUImageuses OpenGL shaders to create dynamic blurs. It has several types of blur: GPUImageBoxBlurFilter, GPUImageFastBlurFilter, GaussianSelectiveBlur, GPUImageGaussianBlurFilter. There is even a GPUImageiOSBlurFilter that “should fully replicate the blur effect provided by iOS 7's control panel” (tweet, article). The article is detailed and informative.
在WWDC 2013 的“在 iOS 上实现引人入胜的 UI”会议中,Apple 解释了如何创建模糊背景(14:30),并提到了
applyLightEffect
使用 Accelerate.framework 在示例代码中实现的方法。GPUImage使用 OpenGL 着色器来创建动态模糊。它有几种类型的模糊:GPUImageBoxBlurFilter、GPUImageFastBlurFilter、GaussianSelectiveBlur、GPUImageGaussianBlurFilter。甚至还有一个 GPUImageiOSBlurFilter,“应该完全复制 iOS 7 控制面板提供的模糊效果”(推文,文章)。这篇文章详细而翔实。
-(UIImage *)blurryGPUImage:(UIImage *)image withBlurLevel:(NSInteger)blur { GPUImageFastBlurFilter *blurFilter = [GPUImageFastBlurFilter new]; blurFilter.blurSize = blur; UIImage *result = [blurFilter imageByFilteringImage:image]; return result; }
From indieambitions.com: Perform a blur using vImage. The algorithm is also used in iOS-RealTimeBlur.
From Nick Lockwood: https://github.com/nicklockwood/FXBlurViewThe example shows the blur over a scroll view. It blurs with dispatch_async, then syncs to call updates with UITrackingRunLoopMode so the blur is not lagged when UIKit gives more priority to the scroll of the UIScrollView. This is explained in Nick's book iOS Core Animation, which btw it's great.
iOS-blurThis takes the blurring layer of the UIToolbar and puts it elsewhere. Apple will reject your app if you use this method. See https://github.com/mochidev/MDBlurView/issues/4
From Evadne blog: LiveFrost: Fast, Synchronous UIView Snapshot Convolving. Great code and a great read. Some ideas from this post:
- Use a serial queue to throttle updates from CADisplayLink.
- Reuse bitmap contexts unless bounds change.
- Draw smaller images using -[CALayer renderInContext:] with a 0.5f scale factor.
来自 indieambitions.com:使用 vImage 执行模糊。该算法也用于iOS-RealTimeBlur。
来自 Nick Lockwood:https: //github.com/nicklockwood/FXBlurView该示例显示了滚动视图上的模糊。它使用 dispatch_async 进行模糊处理,然后使用 UITrackingRunLoopMode 同步调用更新,因此当 UIKit 将更多优先级赋予 UIScrollView 滚动时,模糊不会滞后。这在 Nick 的书iOS Core Animation 中有解释,顺便说一句,它很棒。
iOS-blur这需要 UIToolbar 的模糊层并将其放在其他地方。如果您使用此方法,Apple 将拒绝您的应用程序。见https://github.com/mochidev/MDBlurView/issues/4
来自 Evadne 博客:LiveFrost:快速、同步的 UIView 快照卷积。伟大的代码和伟大的阅读。这篇文章的一些想法:
- 使用串行队列限制来自 CADisplayLink 的更新。
- 除非边界改变,否则重用位图上下文。
- 使用 -[CALayer renderInContext:] 以 0.5f 比例因子绘制较小的图像。
Other stuff
其他的东西
Andy Matuschak saidon Twitter: “you know, a lot of the places where it looks like we're doing it in real time, it's static with clever tricks.”
安迪·马图沙克 (Andy Matuschak)在推特上说:“你知道,很多地方看起来像是我们实时做的,但它是静态的,有巧妙的技巧。”
At doubleencore.comthey say “we've found that a 10 pt blur radius plus a 10 pt increase in saturation best mimics iOS 7's blur effect under most circumstances”.
在doubleencore.com,他们说“我们发现 10 pt 的模糊半径加上 10 pt 的饱和度增加在大多数情况下最能模仿 iOS 7 的模糊效果”。
A peek at the private headers of Apple's SBFProceduralWallpaperView.
看一看Apple 的SBFProceduralWallpaperView的私人标题。
Finally, this isn't a real blur, but remember you can set rasterizationScale to get a pixelated image: http://www.dimzzy.com/blog/2010/11/blur-effect-for-uiview/
最后,这不是真正的模糊,但请记住,您可以设置 rasterizationScale 以获得像素化图像:http: //www.dimzzy.com/blog/2010/11/blur-effect-for-uiview/
回答by valbu17
I decided to post a written Objective-C version from the accepted answer just to provide more options in this question..
我决定从接受的答案中发布一个书面的 Objective-C 版本,只是为了在这个问题中提供更多选项。
- (UIView *)applyBlurToView:(UIView *)view withEffectStyle:(UIBlurEffectStyle)style andConstraints:(BOOL)addConstraints
{
//only apply the blur if the user hasn't disabled transparency effects
if(!UIAccessibilityIsReduceTransparencyEnabled())
{
UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:style];
UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
blurEffectView.frame = view.bounds;
[view addSubview:blurEffectView];
if(addConstraints)
{
//add auto layout constraints so that the blur fills the screen upon rotating device
[blurEffectView setTranslatesAutoresizingMaskIntoConstraints:NO];
[view addConstraint:[NSLayoutConstraint constraintWithItem:blurEffectView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:view
attribute:NSLayoutAttributeTop
multiplier:1
constant:0]];
[view addConstraint:[NSLayoutConstraint constraintWithItem:blurEffectView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:view
attribute:NSLayoutAttributeBottom
multiplier:1
constant:0]];
[view addConstraint:[NSLayoutConstraint constraintWithItem:blurEffectView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:view
attribute:NSLayoutAttributeLeading
multiplier:1
constant:0]];
[view addConstraint:[NSLayoutConstraint constraintWithItem:blurEffectView
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:view
attribute:NSLayoutAttributeTrailing
multiplier:1
constant:0]];
}
}
else
{
view.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.7];
}
return view;
}
The constraints could be removed if you want incase if you only support portrait mode or I just add a flag to this function to use them or not..
如果您只支持纵向模式,或者我只是在此函数中添加一个标志以使用它们,则可以删除这些约束。
回答by xtravar
I don't think I'm allowed to post the code, but the above post mentioning the WWDC sample code is correct. Here is the link: https://developer.apple.com/downloads/index.action?name=WWDC%202013
我不认为我被允许发布代码,但上面提到 WWDC 示例代码的帖子是正确的。这是链接:https: //developer.apple.com/downloads/index.action?name=WWDC%202013
The file you're looking for is the category on UIImage, and the method is applyLightEffect.
你要找的文件是UIImage上的category,方法是applyLightEffect。
As I noted above in a comment, the Apple Blur has saturation and other things going on besides blur. A simple blur will not do... if you are looking to emulate their style.
正如我在上面的评论中指出的那样,Apple Blur 具有饱和度和除模糊之外的其他功能。一个简单的模糊不会做...如果你想模仿他们的风格。
回答by kev
Here's a fast implementation in Swift using CIGaussianBlur:
这是使用 CIGaussianBlur 在 Swift 中的快速实现:
func blur(image image: UIImage) -> UIImage {
let radius: CGFloat = 20;
let context = CIContext(options: nil);
let inputImage = CIImage(CGImage: image.CGImage!);
let filter = CIFilter(name: "CIGaussianBlur");
filter?.setValue(inputImage, forKey: kCIInputImageKey);
filter?.setValue("\(radius)", forKey:kCIInputRadiusKey);
let result = filter?.valueForKey(kCIOutputImageKey) as! CIImage;
let rect = CGRectMake(radius * 2, radius * 2, image.size.width - radius * 4, image.size.height - radius * 4)
let cgImage = context.createCGImage(result, fromRect: rect);
let returnImage = UIImage(CGImage: cgImage);
return returnImage;
}
回答by Sam
I think the easiest solution to this is to override UIToolbar, which blurs everything behind it in iOS 7. It's quite sneaky, but it's very simple for you to implement, and fast!
我认为最简单的解决方案是覆盖 UIToolbar,它在 iOS 7 中模糊了它背后的一切。它非常狡猾,但对您来说实现起来非常简单,而且速度很快!
You can do it with any view, just make it a subclass of UIToolbar
instead of UIView
. You can even do it with a UIViewController
's view
property, for example...
您可以使用任何视图执行此操作,只需将其设为 的子类UIToolbar
而不是UIView
. 你甚至可以用 aUIViewController
的view
属性来做,例如......
1) create a new class that is a "Subclass of" UIViewController
and check the box for "With XIB for user interface".
1)创建一个新类,它是“子类”UIViewController
并选中“使用 XIB 用于用户界面”框。
2) Select the View and go to the identity inspector in the right-hand panel (alt-command-3). Change the "Class" to UIToolbar
. Now go to the attributes inspector (alt-command-4) and change the "Background" color to "Clear Color".
2) 选择视图并转到右侧面板中的身份检查器 (alt-command-3)。将“类”更改为UIToolbar
. 现在转到属性检查器(alt-command-4)并将“背景”颜色更改为“清除颜色”。
3) Add a subview to the main view and hook it up to an IBOutlet in your interface. Call it backgroundColorView
. It will look something like this, as a private category in the implementation (.m
) file.
3) 将子视图添加到主视图并将其连接到界面中的 IBOutlet。叫它backgroundColorView
。它看起来像这样,作为实现 ( .m
) 文件中的私有类别。
@interface BlurExampleViewController ()
@property (weak, nonatomic) IBOutlet UIView *backgroundColorView;
@end
4) Go to the view controller implementation (.m
) file and change the -viewDidLoad
method, to look as follows:
4) 转到视图控制器实现( .m
)文件并更改-viewDidLoad
方法,如下所示:
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.barStyle = UIBarStyleBlack; // this will give a black blur as in the original post
self.backgroundColorView.opaque = NO;
self.backgroundColorView.alpha = 0.5;
self.backgroundColorView.backgroundColor = [UIColor colorWithWhite:0.3 alpha:1];
}
This will give you a dark gray view, which blurs everything behind it. No funny business, no slow core image blurring, using everything that is at your fingertips provided by the OS/SDK.
这会给你一个深灰色的视图,它模糊了它后面的一切。没有有趣的事情,没有缓慢的核心图像模糊,使用 OS/SDK 提供的一切触手可及的东西。
You can add this view controller's view to another view, as follows:
您可以将此视图控制器的视图添加到另一个视图,如下所示:
[self addChildViewController:self.blurViewController];
[self.view addSubview:self.blurViewController.view];
[self.blurViewController didMoveToParentViewController:self];
// animate the self.blurViewController into view
Let me know if anything is unclear, I'll be happy to help!
如果有任何不清楚的地方,请告诉我,我很乐意提供帮助!
Edit
编辑
UIToolbar has been changed in 7.0.3 to give possibly-undesirable effect when using a coloured blur.
UIToolbar 已在 7.0.3 中进行了更改,以在使用彩色模糊时产生可能不受欢迎的效果。
We used to be able to set the colour using barTintColor
, but if you were doing this before, you will need to set the alpha component to less than 1. Otherwise your UIToolbar will be completely opaque colour - with no blur.
我们过去可以使用 设置颜色barTintColor
,但如果您以前这样做,则需要将 alpha 组件设置为小于 1。否则您的 UIToolbar 将是完全不透明的颜色 - 没有模糊。
This can be achieved as follows: (bearing in mind self
is a subclass of UIToolbar
)
这可以通过以下方式实现:(记住self
是 的子类UIToolbar
)
UIColor *color = [UIColor blueColor]; // for example
self.barTintColor = [color colorWithAlphaComponent:0.5];
This will give a blue-ish tint to the blurred view.
这将为模糊的视图提供蓝色调。
回答by Adam Bardon
Here's an easy way to add custom blur without haggling with private APIs using UIViewPropertyAnimator:
这是使用UIViewPropertyAnimator添加自定义模糊而不与私有 API 讨价还价的简单方法:
First, declare class property:
首先,声明类属性:
var blurAnimator: UIViewPropertyAnimator!
Then set your blur view in viewDidLoad()
:
然后将您的模糊视图设置为viewDidLoad()
:
let blurEffectView = UIVisualEffectView()
blurEffectView.backgroundColor = .clear
blurEffectView.frame = view.bounds
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(blurEffectView)
blurAnimator = UIViewPropertyAnimator(duration: 1, curve: .linear) { [blurEffectView] in
blurEffectView.effect = UIBlurEffect(style: .light)
}
blurAnimator.fractionComplete = 0.15 // set the blur intensity.
Note:This solution is not suitable for UICollectionView
/UITableView
cells
注意:此解决方案不适用于UICollectionView
/UITableView
单元格
回答by Hyman
Custom blur scale
自定义模糊比例
You can tryUIVisualEffectView
with custom setting as -
您可以尝试UIVisualEffectView
使用自定义设置作为 -
class BlurViewController: UIViewController {
private let blurEffect = (NSClassFromString("_UICustomBlurEffect") as! UIBlurEffect.Type).init()
override func viewDidLoad() {
super.viewDidLoad()
let blurView = UIVisualEffectView(frame: UIScreen.main.bounds)
blurEffect.setValue(1, forKeyPath: "blurRadius")
blurView.effect = blurEffect
view.addSubview(blurView)
}
}
Output:- for blurEffect.setValue(1...
& blurEffect.setValue(2..
回答by Mehedi Hasan
回答by Aleksandar Vaci?
Accepted answer is correct but there's an important step missing here, in case this view - for which you want blurred background - is presented using
接受的答案是正确的,但这里缺少一个重要的步骤,以防此视图 - 您想要模糊背景 - 使用
[self presentViewController:vc animated:YES completion:nil]
[self presentViewController:vc animated:YES completion:nil]
By default, this will negate the blur as UIKit removes the presenter's view, which you are actually blurring. To avoid that removal, add this line before the previous one
默认情况下,这将消除模糊,因为 UIKit 删除了演示者的视图,您实际上正在模糊它。为避免删除,请在上一行之前添加此行
vc.modalPresentationStyle = UIModalPresentationOverFullScreen;
vc.modalPresentationStyle = UIModalPresentationOverFullScreen;
Or use other Over
styles.
或者使用其他Over
样式。