最近研究了下UIBezierPath,虽然他的构造方法不是特别多,但是感觉还是特别实用的,就是用起来感觉很方便,其主要作用还是用于为视图的Layer层添加路径,相当于根据我们创建的path来对目标视图进行切割.比如说我要把一个视图的形状裁剪一下,或者我想自定义一个几何图形什么的,用UIBezierPath来实现都是很方便的.唯一不方便的地方就是如果要在一个view上只使用UIBezierPath来进行绘制几何图形的话,那么必须要在- (void)drawRect:(CGRect)rect方法里绘制或者调用,这样一来就给我们带来了些许的限制,所以,在日常的开发中,如果要创建自定义的几何图形的话,经常还是使用CAShapeLayer和UIBezierPath共同来创建.各位看官可以看下我的这篇博客:CAShapeLayer的使用,希望能给各位大人带来少许帮助.
不过,今天我们就事论事,只说UIBezierPath的使用,下面是我写的一个Demo,可以先看下效果
这个Demo里包括了UIBezierPath使用频率最高的几种几何图形以及创建的方法,其实UIBezierPath的使用很简单,只不过有些细节需要注意下,下面就先看一下UIBezierPath提供的构造方法(因为对UIBezierPath对象的设置很多地方都是雷同的,所以第一个构造方法我会详细讲解使用方法,后面的几个就不详细讲解了,该Demo我已经上传到github,各位看官可以去github下载完整的工程代码,包括了使用实例,链接我会在后面贴出来):
- +(instancetype)bezierPath;
该方法直接返回一个UIBezierPath对象,不需要传递任何参数,我们可以直接对创建出来的对象操作,使用方法如下:
//绘制一个三角形
-(void)drawtriangle{
//绘制一条完整路径 首先创建路径对象,接着按绘制顺序添加关键点,最后调用[path closePath]方法闭合路径
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(40, self.frame.size.height - 40)];
[path addLineToPoint:CGPointMake(k_ScreenWidth - 40, self.frame.size.height - 40)];
[path addLineToPoint:CGPointMake(k_ScreenWidth/2, 40)];
[path closePath];
/*设置填充颜色 创建一个颜色对象之后,需要调用颜色的set方法设置上下文环境,接着调用路径的fill方法使用上下文环境中的颜色来填充
Tip: 这个fill方法很有意思
如果第一次设置上下文环境为红色,那么调用fill的则会为该路径内填充红色
但是第二次设置上下文环境为绿色时,调用fill方法并不是说将路径内的红色替换掉,而是在红色的上方填充一次绿色
我会在博客里验证,读者也可自行验证
*/
UIColor *redColor = [UIColor redColor];
[redColor set];
[path fill];
//设置线条属性 各种格式我会贴出来给大家看,方便对比
path.lineCapStyle = kCGLineJoinRound; //线段端点格式
path.lineJoinStyle = kCGLineJoinRound; //线段接头格式
path.lineWidth = 8;
//设置路径颜色 原理和设置填充颜色一样,这不过是调用[path stroke]方法来设置路径额颜色 设置线宽为8
UIColor *blackColor = [UIColor blackColor];
[blackColor set];
[path stroke];
}
- 上文提到了lineCapStyle和lineJoinStyle,这两个属性分别对应的是线条端点的风格和线条接头处的风格,有以下三个枚举值:
typedef CF_ENUM(int32_t, CGLineJoin) {
kCGLineJoinMiter,
kCGLineJoinRound,
kCGLineJoinBevel
};
对应的风格如下所示:
端点处的风格:(lineCapStyle)
kCGLineJoinMiter,
kCGLineJoinBevel 这两个风格是一样的,都是直角风格如下所示:
kCGLineJoinRound,圆角风格,如下所示:
接头处的风格:(lineJoinStyle)
kCGLineJoinMiter 斜接
kCGLineJoinRound 圆角
kCGLineJoinBevel 斜角
- +(instancetype)bezierPathWithRect:(CGRect)rect;
该方法会根据传进去的CGRect结构体来创建一个矩形
- +(instancetype)bezierPathWithOvalInRect:(CGRect)rect;
该方法会根据传进去的结构体(矩形)创建一个内切圆,如果该矩形是正方形的话,那么切出来的是一个圆形,如果是长方形的话,切出来的则是一个椭圆 - +(instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius;
该方法会根据传进去的矩形和圆角角度创建一个四个角都是圆角的矩形 - +(instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii;
参数解析:
rect: 基础矩形
corners: 选择矩形中需要添加圆角的位置,该值有四个枚举值,如下所示:
UIRectCornerTopLeft 左上方添加
UIRectCornerTopRight 右上方添加
UIRectCornerBottomLeft 左下方添加
UIRectCornerBottomRight 右下方添加
UIRectCornerAllCorners 全部添加
传值的时候,可以传进去单独的值,如:UIRectCornerTopLeft(在左上角添加圆角),可以传进去多个值,中间使用’|’隔开,如:UIRectCornerTopLeft|UIRectCornerTopRight(给左上角和右上角添加圆角)
ornerRadii:这个参数的意思是,传进去一个矩形A的宽高,以需要添加的角的顶点为矩形A的左上角,在该矩形内做内切圆,为矩形添加圆角. - +(instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;
该方法是用来绘制圆形的
参数解析:
center 圆心位置
radius 半径
startAngle 开始角度,默认从3点钟开始
endAngle 结束角度
clockwise 是否顺时针方向绘制 - +(instancetype)bezierPathWithCGPath:(CGPathRef)CGPath;
该方法和第一个方法类型类似,只不过该方法传进去的是一个CGPath的路径. - 关于fill的讨论
上文中提到,
Tip: 这个fill方法很有意思
如果第一次设置上下文环境为红色,那么调用fill的则会为该路径内填充红色
但是第二次设置上下文环境为绿色时,调用fill方法并不是说将路径内的红色替换掉,而是在红色的上方填充一次绿色
现在我们就来验证一下这个说法,首先,我们绘制一个圆形,代码如下,填充颜色为红色,边框颜色为黑色,线宽30:
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(40, 80, k_ScreenWidth - 80, k_ScreenWidth - 80)];
//设置填充颜色
UIColor *redColor = [UIColor redColor];
[redColor set];
[path fill];
//设置路径格式
path.lineWidth = 30.0;
path.lineCapStyle = kCGLineJoinRound;
path.lineJoinStyle = kCGLineJoinRound;
//设置路径颜色
UIColor *blackColor = [UIColor blackColor];
[blackColor set];
[path stroke];
效果如下:
然后我们在末尾再次填充颜色,执行代码如下:
UIColor *greenColor = [UIColor greenColor];
[greenColor set];
[path fill];
- 效果如下所示:
我们很清楚的可以看出来,边框的宽度减少了一半,原因就是当我们再次调用fill方法的时候,该方法是在原有图层上又添加了一层图像.由于路径的绘制原则是向内外扩展,我们设置的路径宽度是30,那么它就会向内扩展15向外扩展15.当我们再次绘制图层的时候,由于是在原来的基础上填充的,那么就会将向内扩展的15个单位的路径覆盖上,就导致了我们看到路径宽度比原来减少了一半的效果.
- 该工程gitHub地址,各位看官可以去下载下来运行下看看:https://github.com/TheRuningAnt/TestUIBezierPath.git
- 附上文件源代码(部分重要的代码):
//绘制一个三角形 该方法会详细讲述各个参数的意思及使用方法,后面的方法仅仅展示使用方法
-(void)drawtriangle{
//绘制一条完整路径 首先创建路径对象,接着按绘制顺序添加关键点,最后调用[path closePath]方法闭合路径
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(40, self.frame.size.height - 40)];
[path addLineToPoint:CGPointMake(k_ScreenWidth - 40, self.frame.size.height - 40)];
[path addLineToPoint:CGPointMake(k_ScreenWidth/2, 40)];
[path closePath];
/*设置填充颜色 创建一个颜色对象之后,需要调用颜色的set方法设置上下文环境,接着调用路径的fill方法使用上下文环境中的颜色来填充
Tip: 这个fill方法很有意思
如果第一次设置上下文环境为红色,那么调用fill的则会为该路径内填充红色
但是第二次设置上下文环境为绿色时,调用fill方法并不是说将路径内的红色替换掉,而是在红色的上方填充一次绿色
我会在博客里验证,读者也可自行验证
*/
UIColor *redColor = [UIColor redColor];
[redColor set];
[path fill];
//设置线条属性
path.lineCapStyle = kCGLineJoinRound; //线段端点格式
path.lineJoinStyle = kCGLineJoinRound; //线段接头格式
path.lineWidth = 8;
//设置路径颜色 原理和设置填充颜色一样,这不过是调用[path stroke]方法来设置路径额颜色 设置线宽为8
UIColor *blackColor = [UIColor blackColor];
[blackColor set];
[path stroke];
}
#pragma mark:由于设置填充颜色、线条颜色、线条宽度代码重复冗余,所以将其写到一个方法里,统一设置为填充颜色为红色,线条颜色为黑色,线条宽度为8
//绘制一个矩形
-(void)drawRectangle{
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(40,40 ,k_CurrentWidth - 80 , k_CurrentHeight - 80)];
[self setPath:path];
}
//绘制一个实心圆形
-(void)drawFillCircle{
/*
该方法是使用一个矩形为基准绘制其内切圆
当该矩形是正方形时,绘制出的为圆形
当该矩形为长方形的时候,绘制出来的是椭圆
*/
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(40, 80, k_ScreenWidth - 80, k_ScreenWidth - 80)];
[self setPath:path];
}
//绘制一个空心圆形 主要为了展示使用不同的工厂方法来创建圆形
-(void)drawEmptyCircle{
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(k_ScreenWidth/2, k_CurrentHeight - 300) radius:(k_ScreenWidth - 40)/2
startAngle:0
endAngle:k_DegreesToRadians(360)
clockwise:YES];
//设置填充颜色
UIColor *redColor = [UIColor clearColor];
[redColor set];
[path fill];
//设置路径格式
path.lineWidth = 8;
path.lineCapStyle = kCGLineJoinRound;
path.lineJoinStyle = kCGLineJoinRound;
//设置路径颜色
UIColor *blackColor = [UIColor blackColor];
[blackColor set];
[path stroke];
}
//绘制一个四个角都是圆角额矩形
-(void)drawCornerRectangle{
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(40,40 ,k_CurrentWidth - 80 , k_CurrentHeight - 80)
cornerRadius:20];
[self setPath:path];
}
//绘制一个可选角度的矩形
-(void)drawRectWithLeftAndRightCorner{
/*
参数解析:
bezierPathWithRoundedRect 绘制矩形的大小
byRoundingCorners 有哪几个角需要绘制
cornerRadii 圆角角度,使用角的顶点作为圆心来切圆角
*/
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(40,40 ,k_CurrentWidth - 80 , k_CurrentHeight - 80)
byRoundingCorners:UIRectCornerTopLeft|UIRectCornerTopRight
cornerRadii:CGSizeMake((k_CurrentWidth - 80)/2, (k_CurrentWidth - 80)/2)];
[self setPath:path];
}
- (void)drawSecondBezierPath {
UIBezierPath *path = [UIBezierPath bezierPath];
//设置一个起始点
[path moveToPoint:CGPointMake(20, self.frame.size.height - 100)];
// 添加二次曲线
[path addQuadCurveToPoint:CGPointMake(self.frame.size.width - 20, self.frame.size.height - 100)
controlPoint:CGPointMake(self.frame.size.width / 2, 0)];
path.lineCapStyle = kCGLineJoinBevel;
path.lineJoinStyle = kCGLineJoinRound;
path.lineWidth = 8.0;
UIColor *strokeColor = [UIColor blackColor];
[strokeColor set];
[path stroke];
}
- (void)drawThirdBezierPath {
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(20, 200)];
[path addCurveToPoint:CGPointMake(300, 200)
controlPoint1:CGPointMake(160, 50)
controlPoint2:CGPointMake(160, 300)];
path.lineCapStyle = kCGLineCapRound;
path.lineJoinStyle = kCGLineJoinRound;
path.lineWidth = 5.0;
UIColor *strokeColor = [UIColor blackColor];
[strokeColor set];
[path stroke];
}
-(void)setPath:(UIBezierPath*)path{
//设置填充颜色
UIColor *redColor = [UIColor redColor];
[redColor set];
[path fill];
//设置路径格式
path.lineWidth = 8.0;
path.lineCapStyle = kCGLineJoinRound;
path.lineJoinStyle = kCGLineJoinRound;
//设置路径颜色
UIColor *blackColor = [UIColor blackColor];
[blackColor set];
[path stroke];
}