本文参考此文参考bash
咱们在项目中会接触到一些曲线的绘制,最初接触这个概念是由于有一个在屏幕右边的按钮,由于贴着屏幕,按钮右边是没有圆角的,而左上和左下是圆角,相似:app
还没知道贝塞尔曲线绘制以前,我是直接用 _laborExplainButton.layer.cornerRadius = 33/2;
绘制一个四个角都是圆角的按钮,而后给出文字靠左属性 _laborExplainButton.titleLabel.textAlignment = NSTextAlignmentLeft;
设定按钮宽度时,多给出一段宽度,在用Masonary布局时让右边超出父视图,再慢慢调数值,让文字接近居中。布局
方法很笨,可是还算是实现了图片的效果(笑)。学习
可是日后出现的一些相似于tableView的紧密相连的cell的第一个cell左上和右上为圆角的需求,总不能再这样“投机”了,就开始学习到了用贝塞尔曲线实现。动画
本笔记将从苹果官方API:UIBezierPath.h文件的各类方法解释、介绍出发,介绍经常使用的一些方法和流程,再对具体案例作实现介绍,作一个从学到用的总结,不能作到全部方法方面都顾及,更深的更复杂的绘制实现还待后续学习补充。ui
UIBezierPath是在iOS开发中绘制矢量图或者路径的时候会常用的一个部分,在UIKit里面是CoreGraphics对path的封装,使用UIBezierPath能够绘制直线、矩形、椭圆、不规则图形、多边形和贝塞尔曲线等,只要是能想到的线条都能画出来。atom
// 用来对某(几)个角进行贝塞尔绘制
typedef NS_OPTIONS(NSUInteger, UIRectCorner) {
UIRectCornerTopLeft = 1 << 0,
UIRectCornerTopRight = 1 << 1,
UIRectCornerBottomLeft = 1 << 2,
UIRectCornerBottomRight = 1 << 3,
UIRectCornerAllCorners = ~0UL
};
// 初始化无形装的贝塞尔曲线
+ (instancetype)bezierPath;
// 初始化矩形贝塞尔曲线
+ (instancetype)bezierPathWithRect:(CGRect)rect;
// 绘制椭圆(圆形)贝塞尔曲线
+ (instancetype)bezierPathWithOvalInRect:(CGRect)rect;
// 绘制含有圆角的贝塞尔曲线
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius; // rounds all corners with the same horizontal and vertical radius
// 绘制可选择圆角方位的贝塞尔曲线
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii;
// 绘制圆弧曲线
+ (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;
// 根据CGPathRef绘制贝塞尔曲线
+ (instancetype)bezierPathWithCGPath:(CGPathRef)CGPath;
- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
// CGPath能够理解为图形的路径,拿到CGPath
- (CGPathRef)CGPath NS_RETURNS_INNER_POINTER CF_RETURNS_NOT_RETAINED;
// Path construction
// 贝塞尔曲线开始的点
- (void)moveToPoint:(CGPoint)point;
// 添加直线到该点
- (void)addLineToPoint:(CGPoint)point;
// 添加二次曲线到该点
- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2;
// 添加曲线到该点
- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;
// 添加圆弧
- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise NS_AVAILABLE_IOS(4_0);
// 闭合曲线
- (void)closePath;
// 移除全部曲线的点
- (void)removeAllPoints;
// 路径拼接
- (void)appendPath:(UIBezierPath *)bezierPath;
// 返回一个与当前路径相反的新的贝塞尔路径对象
- (UIBezierPath *)bezierPathByReversingPath NS_AVAILABLE_IOS(6_0);
// 路径进行仿射变换
- (void)applyTransform:(CGAffineTransform)transform;
// Path info
// 只读类型,路径上是否有有效的元素
@property(readonly,getter=isEmpty) BOOL empty;
// 和view的bounds是不同的,它获取path的X坐标、Y坐标、宽度,可是高度为0
@property(nonatomic,readonly) CGRect bounds;
// 当前path的位置,能够理解为path的终点
@property(nonatomic,readonly) CGPoint currentPoint;
// 路径是否包含点point
- (BOOL)containsPoint:(CGPoint)point;
// Drawing properties
// 边框高度
@property(nonatomic) CGFloat lineWidth;
// 端点类型
@property(nonatomic) CGLineCap lineCapStyle;
// 线条链接类型
@property(nonatomic) CGLineJoin lineJoinStyle;
// 线条最大宽度最大限制
@property(nonatomic) CGFloat miterLimit; // Used when lineJoinStyle is kCGLineJoinMiter
// 绘制的精度,默认为0.6,精度越大须要处理的时间越长
@property(nonatomic) CGFloat flatness;
// 单双数圈规则是否用于绘制路径,默认是NO
@property(nonatomic) BOOL usesEvenOddFillRule; // Default is NO. When YES, the even-odd fill rule is used for drawing, clipping, and hit testing.
// 设置线型
- (void)setLineDash:(nullable const CGFloat *)pattern count:(NSInteger)count phase:(CGFloat)phase;
// 检索线型
- (void)getLineDash:(nullable CGFloat *)pattern count:(nullable NSInteger *)count phase:(nullable CGFloat *)phase;
// Path operations on the current graphics context
// 填充贝塞尔曲线内部
- (void)fill;
// 绘制贝塞尔曲线边框
- (void)stroke;
// These methods do not affect the blend mode or alpha of the current graphics context
// 过于复杂
- (void)fillWithBlendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;
- (void)strokeWithBlendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;
// 修改当前图形上下文的绘图区域可见,随后的绘图操做致使呈现内容只有发生在指定路径的填充区域
- (void)addClip;
复制代码
UIBezierPath是对CGPathRef的封装,它提供了CGPath属性使咱们在开发过程当中获取底层的path,在建立矢量图形的时候,把图形拆解成一条或者多条线段,而后拼接在一块儿,每条线段的终点都是下一条线段的起点,这就是大概的实现思路。具体步骤以下:spa
一、建立一个UIBezierPath对象;.net
二、用moveToPoint:方法设置初始线段的起点;code
三、添加线段,定义一个或者多个子路径;
四、修改UIBezierPathUIBezierPath的绘图部分的相关属性;
效果图:
图片是个UIImageView,起初对背景的白色view切了四个角的圆角,发现这个UIImageView仍是四角尖尖,再对UIImageView切圆角,变成了四角圆圆....因此,在对背景view切好四个角圆角后,咱们还要用贝塞尔,切UIImageView的上两个角圆角:
可是,我用的是Masonary布局,在懒加载中写贝塞尔相关属性,并不能实现,由于一开始不能获得UIImageView的布局,因此研究了一番,咱们在- (void)layoutSubviews;
方法中才去写切圆角操做,这个方法是在布局完以后会走一次的,很好的解决了取不到范围的问题。
具体:
- (void)layoutSubviews{
[super layoutSubviews];
// 切上上两角圆角
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.coverImageView.bounds byRoundingCorners: UIRectCornerTopLeft|UIRectCornerTopRight cornerRadii:CGSizeMake(5, 5)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
// frame为UIImageView的bounds
maskLayer.frame = self.coverImageView.bounds;
maskLayer.path = maskPath.CGPath;
self.coverImageView.layer.mask = maskLayer;
}
复制代码
项目中其余相似的状况都举一反三,使用此方法解决。
这里实际上是UIBezierPath结合其余layer使用。 原则上使用UIBezierPath主要只是画出形状或画出一个图形的路径path,可是它也能够配合其余的layer使用(CAShapeLayer,CAGradientLayer等),layer能够添加动画,因此UIBezierPath结合layer使用效果会更棒。