iOS初级开发学习笔记:贝塞尔曲线的绘制学习

本文参考此文参考bash

咱们在项目中会接触到一些曲线的绘制,最初接触这个概念是由于有一个在屏幕右边的按钮,由于贴着屏幕,按钮右边是没有圆角的,而左上和左下是圆角,相似:app

还没知道贝塞尔曲线绘制以前,我是直接用 _laborExplainButton.layer.cornerRadius = 33/2; 绘制一个四个角都是圆角的按钮,而后给出文字靠左属性 _laborExplainButton.titleLabel.textAlignment = NSTextAlignmentLeft; 设定按钮宽度时,多给出一段宽度,在用Masonary布局时让右边超出父视图,再慢慢调数值,让文字接近居中。布局

方法很笨,可是还算是实现了图片的效果(笑)。学习

可是日后出现的一些相似于tableView的紧密相连的cell的第一个cell左上和右上为圆角的需求,总不能再这样“投机”了,就开始学习到了用贝塞尔曲线实现。动画


本笔记将从苹果官方API:UIBezierPath.h文件的各类方法解释、介绍出发,介绍经常使用的一些方法和流程,再对具体案例作实现介绍,作一个从学到用的总结,不能作到全部方法方面都顾及,更深的更复杂的绘制实现还待后续学习补充。ui


1、UIBezierPath的概念

UIBezierPath是在iOS开发中绘制矢量图或者路径的时候会常用的一个部分,在UIKit里面是CoreGraphics对path的封装,使用UIBezierPath能够绘制直线、矩形、椭圆、不规则图形、多边形和贝塞尔曲线等,只要是能想到的线条都能画出来。atom

2、UIBezierPath.h内方法介绍

// 用来对某(几)个角进行贝塞尔绘制
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;

复制代码

3、UIBezierPath的使用

UIBezierPath是对CGPathRef的封装,它提供了CGPath属性使咱们在开发过程当中获取底层的path,在建立矢量图形的时候,把图形拆解成一条或者多条线段,而后拼接在一块儿,每条线段的终点都是下一条线段的起点,这就是大概的实现思路。具体步骤以下:spa

一、建立一个UIBezierPath对象;.net

二、用moveToPoint:方法设置初始线段的起点;code

三、添加线段,定义一个或者多个子路径;

四、修改UIBezierPathUIBezierPath的绘图部分的相关属性;

一个cell顶部两角切圆角的简单实现:

效果图:

图片是个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使用效果会更棒。

相关文章
相关标签/搜索