问题描述
我正在构建一个Photo过滤器应用程序(如Instagram,Camera +等等),主屏幕可能是 UIImageView
,它将图像呈现给用户,以及带有一些过滤器和其他选项的底栏。
其中一个选项是模糊,用户可以用手指捏住或移动代表非模糊部分(半径和位置) - 此圆圈外的所有像素都将模糊。
当用户触摸屏幕时,我想在我的图像上方添加一个代表模糊部分的半透明层,其中一个完全透明的圆圈代表非模糊部分。
所以我的问题是,如何添加这一层?我想我需要在我的图像视图上面使用一些视图,并使用一些蒙版来获得我的圆形状?我真的很感激这里的好建议。
还有一件事
我需要圆圈不会直接切割,但有一种渐变渐变。像Instagram这样的东西:
什么是非常重要才能获得良好的效果,我用 drawRect:
成功获得此效果但是旧设备(iphone 4,iPod)的性能非常糟糕
夏普面具
每当你想要绘制一个由一个形状(或一系列形状)组成的路径作为另一个形状的洞时,该键几乎总是使用偶数奇怪的缠绕规则。
来自,可以通过用户迭代重新定位,扩展和签约。
代码
#import< QuartzCore / QuartzCore.h>
@interface ViewController()
@property(强,非原子)IBOutlet UIImageView * imageView;
@property(强)CAShapeLayer * blurFilterMask;
@property(assign)CGPoint blurFilterOrigin;
@property(assign)CGFloat blurFilterDiameter;
@end
@implementation ViewController
//开始模糊屏蔽操作。
- (void)beginBlurMasking
{
self.blurFilterOrigin = self.imageView.center;
self.blurFilterDiameter = MIN(CGRectGetWidth(self.imageView.bounds),CGRectGetHeight(self.imageView.bounds));
CAShapeLayer * blurFilterMask = [CAShapeLayer layer];
//禁用模糊过滤器掩码的path属性的隐式动画。
blurFilterMask.actions = [[NSDictionary alloc] initWithObjectsAndKeys:[NSNull null],@path,nil];
blurFilterMask.fillColor = [UIColor blackColor] .CGColor;
blurFilterMask.fillRule = kCAFillRuleEvenOdd;
blurFilterMask.frame = self.imageView.bounds;
blurFilterMask.opacity = 0.5f;
self.blurFilterMask = blurFilterMask;
[self refreshBlurMask];
[self.imageView.layer addSublayer:blurFilterMask];
UITapGestureRecognizer * tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap :)];
[self.imageView addGestureRecognizer:tapGesture];
UIPinchGestureRecognizer * pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch :)];
[self.imageView addGestureRecognizer:pinchGesture];
}
//将模糊蒙版的原点移动到点击位置。
- (void)handleTap:(UITapGestureRecognizer *)sender
{
self.blurFilterOrigin = [sender locationInView:self.imageView];
[self refreshBlurMask];
}
//展开和收缩模糊蒙版的清晰区域。
- (void)handlePinch:(UIPinchGestureRecognizer *)sender
{
//使用sender.scale和sender.velocity的某种组合来确定你想要扩展圈子的速率/合同。
self.blurFilterDiameter + = sender.velocity;
[self refreshBlurMask];
}
//更新UI中的模糊掩码。
- (void)refreshBlurMask
{
CGFloat blurFilterRadius = self.blurFilterDiameter * 0.5f;
CGMutablePathRef blurRegionPath = CGPathCreateMutable();
CGPathAddRect(blurRegionPath,NULL,self.imageView.bounds);
CGPathAddEllipseInRect(blurRegionPath,NULL,CGRectMake(self.blurFilterOrigin.x - blurFilterRadius,self.blurFilterOrigin.y - blurFilterRadius,self.blurFilterDiameter,self.blurFilterDiameter));
self.blurFilterMask.path = blurRegionPath;
CGPathRelease(blurRegionPath);
}
...
(此图可能有助于理解命名约定代码)
渐变蒙版
详细介绍了如何绘制径向渐变,我们可以用它来创建带有羽毛边缘的遮罩。这可以直接通过子类化来绘制的内容或实施其绘图代表。在这里,我们将其子类化为封装与其相关的数据,即原点和直径。
代码
#import< QuartzCore / QuartzCore.h>
@interface BlurFilterMask:CALayer
@property(assign)CGPoint origin; //模糊滤镜蒙版的中心。
@property(assign)CGFloat直径; //模糊滤镜蒙版的透明区域的直径。
@end
#importBlurFilterMask.h
//渐变点的宽度模糊滤镜蒙版的区域将跨越。
CGFloat const GRADIENT_WIDTH = 50.0f;
@implementation BlurFilterMask
- (void)drawInContext:(CGContextRef)context
{
CGFloat clearRegionRadius = self.diameter * 0.5f;
CGFloat blurRegionRadius = clearRegionRadius + GRADIENT_WIDTH;
CGColorSpaceRef baseColorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat颜色[8] = {0.0f,0.0f,0.0f,0.0f,//清除区域颜色。
0.0f,0.0f,0.0f,0.5f}; //模糊区域颜色。
CGFloat colourLocations [2] = {0.0f,0.4f};
CGGradientRef gradient = CGGradientCreateWithColorComponents(baseColorSpace,colors,colourLocations,2);
CGContextDrawRadialGradient(context,gradient,self.origin,clearRegionRadius,self.origin,blurRegionRadius,kCGGradientDrawsAfterEndLocation);
CGColorSpaceRelease(baseColorSpace);
CGGradientRelease(渐变);
}
@end
#import ViewController.h
#importBlurFilterMask.h
#import< QuartzCore / QuartzCore.h>
@interface ViewController()
@property(强,非原子)IBOutlet UIImageView * imageView;
@property(强)BlurFilterMask * blurFilterMask;
@end
@implementation ViewController
//开始模糊滤镜遮罩操作。
- (void)beginBlurMasking
{
BlurFilterMask * blurFilterMask = [BlurFilterMask layer];
blurFilterMask.diameter = MIN(CGRectGetWidth(self.imageView.bounds),CGRectGetHeight(self.imageView.bounds));
blurFilterMask.frame = self.imageView.bounds;
blurFilterMask.origin = self.imageView.center;
blurFilterMask.shouldRasterize = YES;
[self.imageView.layer addSublayer:blurFilterMask];
[blurFilterMask setNeedsDisplay];
self.blurFilterMask = blurFilterMask;
UITapGestureRecognizer * tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap :)];
[self.imageView addGestureRecognizer:tapGesture];
UIPinchGestureRecognizer * pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch :)];
[self.imageView addGestureRecognizer:pinchGesture];
}
//将模糊蒙版的原点移动到点击位置。
- (void)handleTap:(UITapGestureRecognizer *)sender
{
self.blurFilterMask.origin = [sender locationInView:self.imageView];
[self.blurFilterMask setNeedsDisplay];
}
//展开和收缩模糊蒙版的清晰区域。
- (void)handlePinch:(UIPinchGestureRecognizer *)sender
{
//使用sender.scale和sender.velocity的某种组合来确定你想要扩展掩码的速率/合同。
self.blurFilterMask.diameter + = sender.velocity;
[self.blurFilterMask setNeedsDisplay];
}
...
(此图可能有助于理解命名约定代码)
注意
确保托管图像的
属性设置为 UIImageView
的multiTouchEnabled YES
/ true
:
注意
为了在回答OP问题的清晰度中,这个答案继续使用最初使用的命名约定。这可能会对其他人产生一些误导。 'Mask'就是这个上下文并不是指图像蒙版而是更一般意义上的蒙版。此答案不使用任何图像屏蔽操作。
I'm building a Photo filter app (like Instagram, Camera+ and many more..), may main screen is a UIImageView
that presenting the image to the user, and a bottom bar with some filters and other options.
One of the option is blur, where the user can use his fingers to pinch or move a circle that represent the non-blur part (radius and position) - all the pixels outside of this circle will be blurred.
When the user touch the screen I want to add a semi transparent layer above my image that represent the blurred part, with a fully transparent circle that represent the non-blur part.
So my question is, how do I add this layer? I suppose I need to use some view above my image view, and to use some mask to get my circle shape? I would really appreciate a good tip here.
One More Thing
I need the circle will not be cut straight, but have a kind of gradient fade. something like Instagram:
And what's very important is to get this effect with good performance, I'd succeed getting this effect with drawRect:
but the performance was very bad on old devices (iphone 4, iPod)
Sharp Mask
Whenever you want to draw a path that consists of a shape (or series of shapes) as a hole in another shape, the key is almost always using an 'even odd winding rule'.
From the Winding Rules section of the Cocoa Drawing Guide:
I appreciate that description isn't really helpful without the rules as context and diagrams to make it easier to understand so I urge you to read the links I've provided above. For the sake of creating our circle mask layer the following diagrams depict what an even odd winding rule allows us to accomplish:
Non Zero Winding Rule
Even Odd Winding Rule
Now it's simply a matter of creating the translucent mask using a CAShapeLayer that can be repositioned and expanded and contracted through user iteraction.
Code
#import <QuartzCore/QuartzCore.h>
@interface ViewController ()
@property (strong, nonatomic) IBOutlet UIImageView *imageView;
@property (strong) CAShapeLayer *blurFilterMask;
@property (assign) CGPoint blurFilterOrigin;
@property (assign) CGFloat blurFilterDiameter;
@end
@implementation ViewController
// begin the blur masking operation.
- (void)beginBlurMasking
{
self.blurFilterOrigin = self.imageView.center;
self.blurFilterDiameter = MIN(CGRectGetWidth(self.imageView.bounds), CGRectGetHeight(self.imageView.bounds));
CAShapeLayer *blurFilterMask = [CAShapeLayer layer];
// Disable implicit animations for the blur filter mask's path property.
blurFilterMask.actions = [[NSDictionary alloc] initWithObjectsAndKeys:[NSNull null], @"path", nil];
blurFilterMask.fillColor = [UIColor blackColor].CGColor;
blurFilterMask.fillRule = kCAFillRuleEvenOdd;
blurFilterMask.frame = self.imageView.bounds;
blurFilterMask.opacity = 0.5f;
self.blurFilterMask = blurFilterMask;
[self refreshBlurMask];
[self.imageView.layer addSublayer:blurFilterMask];
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
[self.imageView addGestureRecognizer:tapGesture];
UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)];
[self.imageView addGestureRecognizer:pinchGesture];
}
// Move the origin of the blur mask to the location of the tap.
- (void)handleTap:(UITapGestureRecognizer *)sender
{
self.blurFilterOrigin = [sender locationInView:self.imageView];
[self refreshBlurMask];
}
// Expand and contract the clear region of the blur mask.
- (void)handlePinch:(UIPinchGestureRecognizer *)sender
{
// Use some combination of sender.scale and sender.velocity to determine the rate at which you want the circle to expand/contract.
self.blurFilterDiameter += sender.velocity;
[self refreshBlurMask];
}
// Update the blur mask within the UI.
- (void)refreshBlurMask
{
CGFloat blurFilterRadius = self.blurFilterDiameter * 0.5f;
CGMutablePathRef blurRegionPath = CGPathCreateMutable();
CGPathAddRect(blurRegionPath, NULL, self.imageView.bounds);
CGPathAddEllipseInRect(blurRegionPath, NULL, CGRectMake(self.blurFilterOrigin.x - blurFilterRadius, self.blurFilterOrigin.y - blurFilterRadius, self.blurFilterDiameter, self.blurFilterDiameter));
self.blurFilterMask.path = blurRegionPath;
CGPathRelease(blurRegionPath);
}
...
(This diagram may help understand the naming conventions in the code)
Gradient Mask
The Gradients section of Apple's Quartz 2D Programming Guide details how to draw radial gradients which we can use to create a mask with a feathered edge. This invovlves drawing a CALayers content directly by subclassing it or implementing its drawing delegate. Here we subclass it to encapsulate the data related to it i.e. origin and diameter.
Code
#import <QuartzCore/QuartzCore.h>
@interface BlurFilterMask : CALayer
@property (assign) CGPoint origin; // The centre of the blur filter mask.
@property (assign) CGFloat diameter; // the diameter of the clear region of the blur filter mask.
@end
#import "BlurFilterMask.h"
// The width in points the gradated region of the blur filter mask will span over.
CGFloat const GRADIENT_WIDTH = 50.0f;
@implementation BlurFilterMask
- (void)drawInContext:(CGContextRef)context
{
CGFloat clearRegionRadius = self.diameter * 0.5f;
CGFloat blurRegionRadius = clearRegionRadius + GRADIENT_WIDTH;
CGColorSpaceRef baseColorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat colours[8] = { 0.0f, 0.0f, 0.0f, 0.0f, // Clear region colour.
0.0f, 0.0f, 0.0f, 0.5f }; // Blur region colour.
CGFloat colourLocations[2] = { 0.0f, 0.4f };
CGGradientRef gradient = CGGradientCreateWithColorComponents (baseColorSpace, colours, colourLocations, 2);
CGContextDrawRadialGradient(context, gradient, self.origin, clearRegionRadius, self.origin, blurRegionRadius, kCGGradientDrawsAfterEndLocation);
CGColorSpaceRelease(baseColorSpace);
CGGradientRelease(gradient);
}
@end
#import "ViewController.h"
#import "BlurFilterMask.h"
#import <QuartzCore/QuartzCore.h>
@interface ViewController ()
@property (strong, nonatomic) IBOutlet UIImageView *imageView;
@property (strong) BlurFilterMask *blurFilterMask;
@end
@implementation ViewController
// Begin the blur filter masking operation.
- (void)beginBlurMasking
{
BlurFilterMask *blurFilterMask = [BlurFilterMask layer];
blurFilterMask.diameter = MIN(CGRectGetWidth(self.imageView.bounds), CGRectGetHeight(self.imageView.bounds));
blurFilterMask.frame = self.imageView.bounds;
blurFilterMask.origin = self.imageView.center;
blurFilterMask.shouldRasterize = YES;
[self.imageView.layer addSublayer:blurFilterMask];
[blurFilterMask setNeedsDisplay];
self.blurFilterMask = blurFilterMask;
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
[self.imageView addGestureRecognizer:tapGesture];
UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)];
[self.imageView addGestureRecognizer:pinchGesture];
}
// Move the origin of the blur mask to the location of the tap.
- (void)handleTap:(UITapGestureRecognizer *)sender
{
self.blurFilterMask.origin = [sender locationInView:self.imageView];
[self.blurFilterMask setNeedsDisplay];
}
// Expand and contract the clear region of the blur mask.
- (void)handlePinch:(UIPinchGestureRecognizer *)sender
{
// Use some combination of sender.scale and sender.velocity to determine the rate at which you want the mask to expand/contract.
self.blurFilterMask.diameter += sender.velocity;
[self.blurFilterMask setNeedsDisplay];
}
...
(This diagram may help understand the naming conventions in the code)
Note
Ensure the multipleTouchEnabled
property of the UIImageView
hosting your image is set to YES
/true
:
Note
For sake of clarity in answering the OPs question this answer continues to use the naming conventions originally used. This may be slightly misleading to others. 'Mask' is this context does not refer to an image mask but mask in a more general sense. This answer doesn't use any image masking operations.
这篇关于在UIImageView上添加圆形遮罩层的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!