一、使用CAShapeLayer实现复杂的View的遮罩效果
1.1、案例演示
在聊天界面,发送图片的时候,会有一个三角的指向效果,指向这张图片的发送者。服务端返回给我们的图片只是一张矩形的图片,我们如何把一张矩形的图片或者View,加上一层自定义遮罩效果,就是本文要讲的内容。效果演示如下:第一张是一个View的遮罩效果,第二张是UIImageView的遮罩效果。
1.2、实现机制
在每一View的layer层中有一个mask属性,他就是专门来设置该View的遮罩效果的。该mask本身也是一个layer层。我们只需要生成一个自定义的layer,然后覆盖在需要遮罩的View上面即可。问题就归于如何生成入上图所示的不规则图片的Layer。CAShapeLayer可以根据几个点的依次连线,产生一个闭合空间的layer。如下图所示:
1.3、实现代码
实现CAShapeLayer的ViewMask的Category。
@implementation CAShapeLayer (ViewMask)
+ (instancetype)createMaskLayerWithView : (UIView *)view{
CGFloat viewWidth = CGRectGetWidth(view.frame);
CGFloat viewHeight = CGRectGetHeight(view.frame);
CGFloat rightSpace = 10.;
CGFloat topSpace = 15.;
CGPoint point1 = CGPointMake(0, 0);
CGPoint point2 = CGPointMake(viewWidth-rightSpace, 0);
CGPoint point3 = CGPointMake(viewWidth-rightSpace, topSpace);
CGPoint point4 = CGPointMake(viewWidth, topSpace);
CGPoint point5 = CGPointMake(viewWidth-rightSpace, topSpace+10.);
CGPoint point6 = CGPointMake(viewWidth-rightSpace, viewHeight);
CGPoint point7 = CGPointMake(0, viewHeight);
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:point1];
[path addLineToPoint:point2];
[path addLineToPoint:point3];
[path addLineToPoint:point4];
[path addLineToPoint:point5];
[path addLineToPoint:point6];
[path addLineToPoint:point7];
[path closePath];
CAShapeLayer *layer = [CAShapeLayer layer];
layer.path = path.CGPath;
return layer;
}
@end
//调用方式
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(40, 50, 80, 100)];
view.backgroundColor = [UIColor orangeColor];
[self.view addSubview:view];
CAShapeLayer *layer = [CAShapeLayer createMaskLayerWithView:view];
view.layer.mask = layer;
二、使用CAShapeLayer实现一个音量大小动态改变的控件
2.1、案例演示
对于实时显示语音音量大小的需求,发现很多人的实现方式通过预放置多张图进行切换进行完成的。这样的处理,不但会浪费App的资源存储空间,而且效率也不高。对于符合某一定规律动态改变的图形,我们也可以考虑通过代码的方式来实现。
2.2、实现机制
外部轮廓View主要控制显示大小和显示的圆角效果。内部的Layer主要控制动态显示的高度,虽然他是矩形的。但是当把该Layer加入到View中,而该View设置了_dynamicView.clipsToBounds = YES;。内部的Layer超过外部轮廓的部分,则会被切除掉。
如此说来,我们只需要动态改变内部Layer显示的高度,即可完成该效果显示。
2.3、实现代码
/_dynamicView 表示外部轮廓的View。
//indicateLayer 表示内容动态显示的Layer。
//方法如下:
-(void)refreshUIWithVoicePower : (NSInteger)voicePower{
CGFloat height = (voicePower)*(CGRectGetHeight(_dynamicView.frame)/TOTAL_NUM);
[_indicateLayer removeFromSuperlayer];
_indicateLayer = nil;
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, CGRectGetHeight(_dynamicView.frame)-height, CGRectGetWidth(_dynamicView.frame), height) cornerRadius:0];
_indicateLayer = [CAShapeLayer layer];
_indicateLayer.path = path.CGPath;
_indicateLayer.fillColor = [UIColor whiteColor].CGColor;
[_dynamicView.layer addSublayer:_indicateLayer];
}
三、圆形进度条
3.1、案例演示
实现效果如下:
在不知道有CAShapeLayer的strokeStart和strokeEnd属性的时候,可以想到的方法就是实时的 移除旧的CAShapeLayer 然后重绘这个圆形的CAShapeLayer。显然这种方式的效率是不高的。后来在一次看别人Demo的时候,发现别人使用了CAShapeLayer的strokeStart和strokeEnd属性,实现这一个效果十分的简单方便。下面就和大家来讲一讲这两个属性的使用。
3.2、属性详解
苹果官方给出这两个属性的解释为:
These values define the subregion of the path used to draw the stroked outline. The values must be in the range [0,1] with zero representing the start of the path and one the end. Values in between zero and one are interpolated linearly along the path length. strokeStart defaults to zero and strokeEnd to one. Both are animatable.
大概意思就是:我们可以对绘制的Path进行分区。这两个属性的值在0~1之间,0代表Path的开始位置,1代表Path的结束位置。是一种线性递增关系。strokeStart默认值为0,strokeEnd默认值为1。这两个属性都支持动画。
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.frame = _demoView.bounds;
shapeLayer.strokeEnd = 0.7f;
shapeLayer.strokeStart = 0.1f;
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:_demoView.bounds];
shapeLayer.path = path.CGPath;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.lineWidth = 2.0f;
shapeLayer.strokeColor = [UIColor redColor].CGColor;
[_demoView.layer addSublayer:shapeLayer];
通过以上代码设置:strokeStart=0.1f; strokeEnd=0.7f则显示如下图所示。
画奇怪图像
- (void)test{
UIView *views = [[UIView alloc]init];
views.frame = CGRectMake(20, 100, Screen_Width - 40, 400);
views.backgroundColor = [UIColor orangeColor];
[self.view addSubview:views];
views.layer.mask = [self createMaskLayerWithView:views];
}
- (CAShapeLayer *)createMaskLayerWithView : (UIView *)view{
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(0,70)];
[bezierPath addArcWithCenter:CGPointMake(20, 90) radius:20 startAngle:M_PI endAngle:M_PI/2 * 3 clockwise:YES];
CGPoint point1 = CGPointMake((Screen_Width - 40)/2 - 70, 70);
[bezierPath addLineToPoint:point1];
[bezierPath addQuadCurveToPoint:CGPointMake((Screen_Width - 40)/2 + 70 , 70) controlPoint:CGPointMake((Screen_Width - 40)/2, 0)];
CGPoint point6 = CGPointMake((Screen_Width - 40), 70);
CGPoint point7 = CGPointMake((Screen_Width - 40), 400);
CGPoint point8 = CGPointMake(0, 400);
[bezierPath addLineToPoint:point6];
[bezierPath addArcWithCenter:CGPointMake(Screen_Width - 40 - 20, 90) radius:20 startAngle:M_PI/2 * 3 endAngle:M_PI * 2 clockwise:YES];
[bezierPath addArcWithCenter:CGPointMake(Screen_Width - 40 - 20, 400 - 20) radius:20 startAngle:0 endAngle:M_PI / 2 clockwise:YES];
[bezierPath addLineToPoint:point7];
[bezierPath addArcWithCenter:CGPointMake(20, 400 - 20) radius:20 startAngle:M_PI / 2 endAngle:M_PI clockwise:YES];
[bezierPath addLineToPoint:point8];
[bezierPath closePath];
CAShapeLayer *layer = [CAShapeLayer layer];
layer.path = bezierPath.CGPath;
return layer;
}
3.3、圆形进度条的实现代码
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.frame = _demoView.bounds;
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:_demoView.bounds];
shapeLayer.path = path.CGPath;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.lineWidth = 2.0f;
shapeLayer.strokeColor = [UIColor redColor].CGColor;
[_demoView.layer addSublayer:shapeLayer];
CABasicAnimation *pathAnima = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
pathAnima.duration = 3.0f;
pathAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
pathAnima.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnima.toValue = [NSNumber numberWithFloat:1.0f];
pathAnima.fillMode = kCAFillModeForwards;
pathAnima.removedOnCompletion = NO;
[shapeLayer addAnimation:pathAnima forKey:@"strokeEndAnimation"];
原文链接:关于CAShapeLayer的一些实用案例和技巧 - CocoaChina_一站式开发者成长社区
相关博文:放肆地使用UIBezierPath和CAShapeLayer画各种图形 - CocoaChina_一站式开发者成长社区
UIView *views = [[UIView alloc]init];
views.frame = CGRectMake(0, 100, Screen_Width, 500);
views.backgroundColor = [UIColor orangeColor];
[self.view addSubview:views];
- (CAShapeLayer *)createMaskLayerWithView : (UIView *)view{
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(0,300)];
[bezierPath addQuadCurveToPoint:CGPointMake(Screen_Width, 300) controlPoint:CGPointMake(Screen_Width/2, 400)];
CGPoint point6 = CGPointMake(Screen_Width, 0);
CGPoint point7 = CGPointMake(0, 0);
[bezierPath addLineToPoint:point6];
[bezierPath addLineToPoint:point7];
[bezierPath closePath];
CAShapeLayer *layer = [CAShapeLayer layer];
layer.path = bezierPath.CGPath;
return layer;
}
UIView *views = [[UIView alloc]init]; views.frame = CGRectMake(20, 100, 300, 300); views.backgroundColor = [UIColor orangeColor]; [self.view addSubview:views]; views.layer.mask = [self createMaskLayerWithView:views];
}
- (CAShapeLayer *)createMaskLayerWithView : (UIView *)view{ UIBezierPath *bezierPath = [UIBezierPath bezierPath]; [bezierPath moveToPoint:CGPointMake(0,0)]; [bezierPath addArcWithCenter:CGPointMake(150, 0) radius:40 startAngle:M_PI endAngle: 0 clockwise:NO]; [bezierPath addLineToPoint:CGPointMake(300,0)]; [bezierPath addArcWithCenter:CGPointMake(300, 150) radius:40 startAngle:M_PI_2 * 3 endAngle:M_PI_2 clockwise:NO]; [bezierPath addLineToPoint:CGPointMake(300,300)]; [bezierPath addLineToPoint:CGPointMake(0, 300)];
[bezierPath closePath]; CAShapeLayer *layer = [CAShapeLayer layer]; layer.path = bezierPath.CGPath; return layer; }
|
UIBezierPath用于定义一个直线/曲线组合而成的路径,并且可以在自定义视图中渲染该路径。
注意:使用UIBezierPath绘画的代码写在自定义视图的drawRect:方法中。
一、创建UIBezierPath.
+ (instancetype)bezierPath;
初始化一个UIBezierPath对象。
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
+ (instancetype)bezierPathWithRect:(CGRect)rect;
以CGRect为范围,创建一个矩形路径。
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRect:CGRectMake(100, 100, 200, 300)];
+ (instancetype)bezierPathWithOvalInRect:(CGRect)rect;
以CGRect为范围,创建一个圆/椭圆。
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(100, 100, 200, 300)];
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius;
以CGRect为大小,以cornerRadius为圆角半径,绘制一个圆角矩形。
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(100, 100, 200, 300) cornerRadius:10.f];
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii;
绘制一个圆角矩形,通过UIRectCorner选择圆弧的位置,cornerRadii为圆弧的大小。
注:UIRectCorner的值:
typedefNS_OPTIONS(NSUInteger, UIRectCorner) {
UIRectCornerTopLeft =1<<0, //矩形左上角
UIRectCornerTopRight =1<<1, //矩形右上角
UIRectCornerBottomLeft =1<<2, //矩形左下角
UIRectCornerBottomRight =1<<3, //矩形右下角
UIRectCornerAllCorners = ~0UL //矩形四个角都包括
};
注意:cornerRadii为圆弧半径,圆弧以cornerRadii宽、高的值大的为准,如果超过其邻近最短边的一半,则已最短边一半为准。(自己试出来的)
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(100, 100, 200, 300) byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight | UIRectCornerBottomLeft cornerRadii:CGSizeMake(20, 40)];
+ (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;
绘制一个圆弧路径。ArcCenter:圆弧圆心位置;radius:圆弧半径;startAngle:开始的弧度(角度);endAngle:结束的弧度(角度);clockwise:是否为顺时针。
注意:iPhone中,左上角为原点,x轴向右为正方向;y轴向下为正方向。
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithArcCenter:self.view.center radius:100 startAngle:M_PI_2 endAngle: 2 * M_PI clockwise:YES];
+ (instancetype)bezierPathWithCGPath:(CGPathRef)CGPath;
根据CGPath创建一个新的UIBezierPath。
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
UIBezierPath *newPath = [UIBezierPath bezierPathWithCGPath:bezierPath.CGPath];
二、路径操作函数
- (void)moveToPoint:(CGPoint)point;
将当前点移动到指定的位置。
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(100, 300)];
- (void)addLineToPoint:(CGPoint)point;
在路径中增加一条直线(起点+终点=一条直线)
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(100,300)];
[bezierPath addLineToPoint:CGPointMake(150, 150)];
- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2;
绘制三阶贝塞尔曲线。以endPoint为终点,以controlPoint1、controlPoint2两个点为控制点,绘制贝塞尔曲线。
三阶贝塞尔曲线原理图
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(100,300)];
[bezierPath addCurveToPoint:CGPointMake(300, 300) controlPoint1:CGPointMake(150, 150) controlPoint2:CGPointMake(220, 360)];
三阶贝塞尔曲线
- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;
绘制二阶贝塞尔曲线。以endPoint为终点,以controlPoint为控制点,绘制二阶贝塞尔曲线。
二阶贝塞尔曲线原理图
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(100,300)];
[bezierPath addQuadCurveToPoint:CGPointMake(300, 300) controlPoint:CGPointMake(150, 150)];
二阶贝塞尔曲线
- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise NS_AVAILABLE_IOS(4_0);
绘制一条圆弧。ArcCenter:圆弧圆心位置;radius:圆弧半径;startAngle:开始的弧度(角度);endAngle:结束的弧度(角度);clockwise:是否为顺时针。
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(100,300)];
[bezierPath addArcWithCenter:self.view.center radius:100 startAngle:M_PI_2 endAngle:2 * M_PI clockwise:YES];
注:因为圆弧的中心和起点不是一个位置,所以效果图中多了一个从起点到圆弧开始点的直线。
效果图
- (void)closePath;
使用一条直线闭合路径的起点和终点, 该方法同时也会更新当前点到新直线的终点(即路径的起点)(使一个路径变成闭合回路)。
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(100,300)];
[bezierPath addLineToPoint:CGPointMake(150, 50)];
[bezierPath addLineToPoint:CGPointMake(220, 80)];
[bezierPath closePath];
[bezierPath addLineToPoint:CGPointMake(250, 430)];
注:在效果图中可以看出,在调用closePath方法之后,路径形成了一个封闭的三角形,之后再添加直线,也是从起点开始,而不是上一个终点。
效果图
- (void)removeAllPoints;
移除路径中所有的点。
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath removeAllPoints];
- (void)appendPath:(UIBezierPath *)bezierPath;
路径中增加一个已有路径。
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
UIBezierPath *path2 = [UIBezierPath bezierPath];
[bezierPath appendPath:path2];
- (UIBezierPath *)bezierPathByReversingPath NS_AVAILABLE_IOS(6_0);
返回一个翻转已有路径的新路径。
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
UIBezierPath*path2 = [bezierPath bezierPathByReversingPath];
- (void)applyTransform:(CGAffineTransform)transform;
对路径中的所有点进行二维形变, 该变化立即生效, 且为永久性改变所有点。
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath applyTransform:CGAffineTransformMakeTranslation(20, 20)];
三、路径信息属性
@property(nonatomic) CGPathRef CGPath;
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
CGPathRefpath = bezierPath.CGPath;
@property(readonly,getter=isEmpty) BOOL empty;
是否路径信息为空, 即使通过moveToPoint:移动到指定的位置也算不为空。
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
BOOLisEmpty = bezierPath.empty;
@property(nonatomic,readonly) CGRect bounds;
可以封闭所有路径点的最小矩形范围, 包括多次贝塞尔曲线的控制点在内。
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
CGRect bounds = bezierPath.bounds;
@property(nonatomic,readonly) CGPoint currentPoint;
路径当前所在点。
UIBezierPath*path = [UIBezierPathbezierPath];
CGPoint currentPoint = path.currentPoint;
- (BOOL)containsPoint:(CGPoint)point;
是否包含指定点。
UIBezierPath*path = [UIBezierPathbezierPath];
BOOL isContainsPoint = [path containsPoint:CGPointMake(20,30)];
四、绘图相关方法和属性
@property(nonatomic) CGFloat lineWidth;
路径的线宽。
UIBezierPath*path = [UIBezierPath bezierPath];
path.lineWidth =10;
@property(nonatomic) CGLineCap lineCapStyle;
路径的终点形状, 该属性适用于开放路径的起点和终点。
注意:lineCapStyle的值:
typedefCF_ENUM(int32_t, CGLineCap) {
kCGLineCapButt, //方形结束, 结束位置正好为精确位置。——默认值
kCGLineCapRound, //圆形结束, 结束位置超过精确位置半个线宽。
kCGLineCapSquare //方形结束, 结束位置超过精确位置半个线宽。
};
UIBezierPath*path = [UIBezierPath bezierPath];
path.lineCapStyle = kCGLineCapButt;
@property(nonatomic) CGLineJoin lineJoinStyle;
路径的连接点(拐角)形状。
注意:lineJoinStyle的值:
typedefCF_ENUM(int32_t, CGLineJoin) {
kCGLineJoinMiter, //全部连接(尖角)。——默认值
kCGLineJoinRound, //圆形连接。(圆角)
kCGLineJoinBevel //斜角连接。(切角)
};
拐角样式
UIBezierPath*path = [UIBezierPath bezierPath];
path.lineJoinStyle = kCGLineJoinMiter;
@property(nonatomic) CGFloat miterLimit; // Used when lineJoinStyle is kCGLineJoinMiter
最大斜接长度,怎么理解呢?就是上图中拐角处外面尖角和内部尖角的距离。但是这个只有在kCGLineJoinMiter情况下使用才有效果,如果这个miterLimit小于斜接长度,就成为了kCGLineJoinBevel类型。
UIBezierPath*path = [UIBezierPath bezierPath];
path.lineJoinStyle = kCGLineJoinMiter;
path.miterLimit = 1; ////这里设为1 因为斜接长度超过了1 所以就自动转化为了kCGLineJoinBevel类型。
@property(nonatomic) CGFloat flatness;
确定弯曲路径短的绘制精度的因素。
@property(nonatomic) BOOL usesEvenOddFillRule; // Default is NO. When YES, the even-odd fill rule is used for drawing, clipping, and hit testing.
一个bool值指定even-odd规则是否在path可用。
- (void)setLineDash:(nullableconstCGFloat *)pattern count:(NSInteger)count phase:(CGFloat)phase;
设置线型 可设置成虚线。
注:pattern 线段数组 如:CGFloat dash[] = {1,1}; 代表实线和空白交替的长度 及先绘制1个长度再空1个,再绘制一个.....;
count数组长度 count值小于数组实际长度时,方法就会对相应长度的数组元素进行循环,而大于的时候 会有警告,没有效果;
phase 循环数组时跳过的长度,如pattern为{2,6},phase为1,则第一个元素画1的时候就跳过直接画6
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(100,300)];
[bezierPath addLineToPoint:CGPointMake(150, 50)];
CGFloat patten[] = {4,6};
[bezierPath setLineDash:pattencount:2phase:1];
效果图
- (void)getLineDash:(nullableCGFloat *)pattern count:(nullableNSInteger *)count phase:(nullableCGFloat *)phase;
检索线型。
- (void)fill;
利用当前绘画属性填充路径封闭范围, 该方法在绘画之前会自动将开放子路径封闭, 填充部分不包含路径本身, 所以对于线宽较大的路径, 填充部分会跟部分路径重合。
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(100,300)];
[bezierPath addLineToPoint:CGPointMake(150, 50)];
[bezierPath addLineToPoint:CGPointMake(220, 80)];
bezierPath.lineWidth=5.0;
[bezierPath fill];
[bezierPath stroke];
- (void)stroke;
利用当前绘画属性沿着路径画线。
UIBezierPath*path = [UIBezierPathbezierPath];
// do something....
[path stroke];
- (void)fillWithBlendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;
利用指定模式填充路径封闭范围, 该方法在绘画之前会自动将开放子路径封闭, 填充部分不包含路径本身, 所以对于线宽较大的路径, 填充部分会跟部分路径重合。
注意:CGBlendMode的值很多,大家有兴趣的可以自己挨个试试。
typedefCF_ENUM (int32_t, CGBlendMode) {
/* Available in Mac OS X 10.4 & later. */
kCGBlendModeNormal,
kCGBlendModeMultiply,
kCGBlendModeScreen,
kCGBlendModeOverlay,
kCGBlendModeDarken,
kCGBlendModeLighten,
kCGBlendModeColorDodge,
kCGBlendModeColorBurn,
kCGBlendModeSoftLight,
kCGBlendModeHardLight,
kCGBlendModeDifference,
kCGBlendModeExclusion,
kCGBlendModeHue,
kCGBlendModeSaturation,
kCGBlendModeColor,
kCGBlendModeLuminosity,
/* Available in Mac OS X 10.5 & later. R, S, and D are, respectively,
premultiplied result, source, and destination colors with alpha; Ra,
Sa, and Da are the alpha components of these colors.
The Porter-Duff "source over" mode is called `kCGBlendModeNormal':
R = S + D*(1 - Sa)
Note that the Porter-Duff "XOR" mode is only titularly related to the
classical bitmap XOR operation (which is unsupported by
CoreGraphics). */
kCGBlendModeClear, /* R = 0 */
kCGBlendModeCopy, /* R = S */
kCGBlendModeSourceIn, /* R = S*Da */
kCGBlendModeSourceOut, /* R = S*(1 - Da) */
kCGBlendModeSourceAtop, /* R = S*Da + D*(1 - Sa) */
kCGBlendModeDestinationOver, /* R = S*(1 - Da) + D */
kCGBlendModeDestinationIn, /* R = D*Sa */
kCGBlendModeDestinationOut, /* R = D*(1 - Sa) */
kCGBlendModeDestinationAtop, /* R = S*(1 - Da) + D*Sa */
kCGBlendModeXOR, /* R = S*(1 - Da) + D*(1 - Sa) */
kCGBlendModePlusDarker, /* R = MAX(0, (1 - D) + (1 - S)) */
kCGBlendModePlusLighter /* R = MIN(1, S + D) */
};
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(100,300)];
[bezierPath addLineToPoint:CGPointMake(150, 50)];
[bezierPath addLineToPoint:CGPointMake(220, 80)];
bezierPath.lineWidth=5.0;
[bezierPath fillWithBlendMode:kCGBlendModeNormal alpha:0.4];
[bezierPath stroke];
效果图
- (void)strokeWithBlendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;
利用指定模式沿着路径画线。
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(100,300)];
[bezierPath addLineToPoint:CGPointMake(150, 50)];
[bezierPath addLineToPoint:CGPointMake(220, 80)];
bezierPath.lineWidth=5.0;
[bezierPath strokeWithBlendMode:kCGBlendModeNormal alpha:0.4];
效果图
- (void)addClip;
剪切被接收者路径包围的区域该路径是带有剪切路径的当前绘图上下文。使得其成为我们当前的剪切路径。简单的说,就是一个path调用addClip之后,它所在的context的可见区域就变成了它的“fill area”,接下来的绘制,如果在这个区域外都会被无视。
//首先画一个三角形
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(100,300)];
[bezierPath addLineToPoint:CGPointMake(150, 50)];
[bezierPath addLineToPoint:CGPointMake(220, 80)];
bezierPath.lineWidth=5.0;
[bezierPath fillWithBlendMode:kCGBlendModeNormal alpha:0.4];
//然后调用addClip(裁剪)方法。
[bezierPath addClip];
//之后,在绘制一个圆弧。
[bezierPath addArcWithCenter:bezierPath.currentPoint radius:100 startAngle:M_PI_2 endAngle:2 * M_PI clockwise:YES];
[bezierPath stroke];
效果图上可以看出圆弧只显示了在三角形上的一部分。
效果图
- (void)setFill;
路径的填充颜色。
[[UIColor orangeColor] setFill];
- (void)setStroke;
路径的画线颜色。
[[UIColor orangeColor] setStroke];
作者:光之盐汽水
链接:https://www.jianshu.com/p/e136c3e65c29
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
首页图片.jpg
最近在学核心动画的时候遇到了遇到了fillMode的属性,除了kCAFillModeForwards比较理解意外,其他三个参数一脸蒙蔽,感觉没什么区别,看官方文档也没看出个所以然,去网上查了一些资料如下:
kCAFillModeBackwards 这个和kCAFillModeForwards是相对的,就是在动画开始前,你只要将动画加入了一个layer,layer便立即进入动画的初始状态并等待动画开始.你可以这样设定测试代码,将一个动画加入一个layer的时候延迟5秒执行.然后就会发现在动画没有开始的时候,只要动画被加入了layer,layer便处于动画初始状态
而网上所有的哥们都是照抄这句话,根本不理解啊有木有!后来通过查资料明白了其中的真理,删除线中的话很容易把人带到坑里面--!看到删除线中的话我一直在延迟把layer加到动画中,然后我就掉坑里面去了,后来想通了,应该是让动画延迟5秒执行(开始),这样就能看出效果了。
在说fillMode之前首先要说说removedOnCompletion属性
removedOnCompletion:默认为YES,代表动画执行完毕后就从图层上移除,图形会恢复到动画执行前的状态。如果想让图层保持显示动画执行后的状态,那就设置为NO,不过还要设置fillMode为kCAFillModeForwards
接下来说说fillMode属性
fillMode:决定当前对象在非active时间段的行为.比如动画开始之前,动画结束之后
kCAFillModeRemoved 这个是默认值,也就是说当动画开始前和动画结束后,动画对layer都没有影响,动画结束后,layer会恢复到之前的状态(可以理解为动画执行完成后移除)
kCAFillModeForwards 当动画结束后,layer会一直保持着动画最后的状态
kCAFillModeBackwards 当在动画开始前,你只要把layer加入到一个动画中,layer便立即进入动画的初始状态并等待动画开始.你可以这样设定测试代码,延迟3秒让动画开始,只要动画被加入了layer,layer便处于动画初始状态
当点击屏幕的时候让动画3秒后执行
核心代码
//kCAFillModeForwards
CABasicAnimation * basicAnimation = [[CABasicAnimation alloc] init];
basicAnimation.keyPath = @"position.y";
basicAnimation.removedOnCompletion = NO;
//beginTime : 动画的开始时间
//CACurrentMediaTime() : 图层的当前时间
//让动画延迟3秒执行
basicAnimation.beginTime = CACurrentMediaTime()+3;
basicAnimation.fillMode = kCAFillModeForwards;
//动画的开始位置
basicAnimation.fromValue = @300;
//动画的结束位置
basicAnimation.toValue = @500;
//动画持续时间
basicAnimation.duration = 3;
[self.layer addAnimation:basicAnimation forKey:nil];
效果
练习1.gif
核心代码
CABasicAnimation * basicAnimation = [[CABasicAnimation alloc] init];
basicAnimation.keyPath = @"position.y";
//beginTime : 动画的开始时间
//CACurrentMediaTime() : 图层的当前时间
//让动画延迟3秒执行
basicAnimation.beginTime = CACurrentMediaTime()+3;
basicAnimation.fillMode = kCAFillModeBackwards;
//动画的开始位置
basicAnimation.fromValue = @300;
//动画的结束位置
basicAnimation.toValue = @500;
//动画持续时间
basicAnimation.duration = 3;
[self.layer addAnimation:basicAnimation forKey:nil];
效果
练习2.gif
相信大家从效果能看出来kCAFillModeForwards是在动画开始之后layer迅速进入指定位置开始执行动画并在removedOnCompletion = NO的前提下会停在动画结束的最后位置,而kCAFillModeBackwards则是在动画开始之前迅速进入指定状态并在动画开始后执行动画,动画结束后迅速返回到layer的本身位置
如果kCAFillModeForwards和kCAFillModeBackwards区分后那kCAFillModeBoth就很好理解了如果removedOnCompletion = NO那layer会在动画开始之前就会迅速进入动画的初始位置并在执行完动画后停在动画结束的位置,如果removedOnCompletion = YES那layer会在动画开始之前就会迅速进入动画的初始位置并在执行完动画后迅速返回到layer的本身位置
代码比较简单,如果有需要源码的同学请在评论区留言。
转载请注明出处~~(尊重原创?)
作者:月下独酌灬
链接:https://www.jianshu.com/p/91fccd32f6fb
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。