iOS初级开发学习笔记:贝塞尔曲线的绘制学习
发布日期:2021-08-21 12:05:59 浏览次数:3 分类:技术文章

本文共 5391 字,大约阅读时间需要 17 分钟。

本文参考此文

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

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

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

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


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


一、UIBezierPath的概念

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

二、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;复制代码

三、UIBezierPath的使用

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

1、创建一个UIBezierPath对象;

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

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

4、修改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使用效果会更棒。

转载于:https://juejin.im/post/5d062483e51d4510a73280b3

转载地址:https://blog.csdn.net/weixin_33717298/article/details/93178209 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:http协议
下一篇:【题解】黑白 Nim (2019,5.23)

发表评论

最新留言

第一次来,支持一个
[***.219.124.196]2024年04月02日 15时19分56秒