iOS - 用UIBezierPath介绍和实现简单的扇形下载进度条

所写知识点来自《小码哥视频教程》

吐槽:实习生太苦b

相关知识点:

  • DrawRect方法的使用
  • 常见图形的绘制:线条、多边形、圆
  • 绘图状态的设置:文字颜色、线宽等
  • 图形上下文状态的保存和恢复(图形上下文栈)
  • 图片的裁剪
  • 截图
一、简单介绍Quartz2D

1. 什么是Quartz2D?
Quartz 2D是一个二维绘图引擎,同时支持iOS和Mac系统

2. Quartz 2D能完成的工作
绘制图形 : 线条\三角形\矩形\圆\弧等
绘制文字
绘制\生成图片(图像)
读取\生成PDF
截图\裁剪图片
自定义UI控件
...

二、Quartz2D实例

只要是往View中画东西,必须得在- (void)drawRect:(CGRect)rect;方法中去写

1. 基本路径绘制

  • DrawRect在程序中的调用顺序:
    viewWillAppear -> drawRect -> viewDidAppear
    即将显示和显示完毕之间
- (void)drawRect:(CGRect)rect {
    NSLog(@"%s",__func__) ;
}
控制器中

2. UIBezierPath对象相关的方法和属性

  • UIBezierPath创建的类方法
// 创建基本路径
+ (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;
// 通过CGPath创建
+ (instancetype)bezierPathWithCGPath:(CGPathRef)CGPath;
  • UIBezierPath相关属性和方法
    • 属性
// 与之对应的CGPath
@property(nonatomic) CGPathRef CGPath;
- (CGPathRef)CGPath NS_RETURNS_INNER_POINTER CF_RETURNS_NOT_RETAINED;
// 是否为空
@property(readonly,getter=isEmpty) BOOL empty;
// 整个路径相对于原点的位置及宽高
@property(nonatomic,readonly) CGRect bounds;
// 当前画笔位置
@property(nonatomic,readonly) CGPoint currentPoint;

// 线宽
@property(nonatomic) CGFloat lineWidth;

// 终点类型
@property(nonatomic) CGLineCap lineCapStyle; 
typedef CF_ENUM(int32_t, CGLineCap) {
    kCGLineCapButt,
    kCGLineCapRound,
    kCGLineCapSquare
};

// 交叉点的类型
@property(nonatomic) CGLineJoin lineJoinStyle; 
typedef CF_ENUM(int32_t, CGLineJoin) {
    kCGLineJoinMiter,
    kCGLineJoinRound,
    kCGLineJoinBevel
};

// 两条线交汇处内角和外角之间的最大距离,需要交叉点类型为kCGLineJoinMiter是生效,最大限制为10
@property(nonatomic) CGFloat miterLimit; 
// 个人理解为绘线的精细程度,默认为0.6,数值越大,需要处理的时间越长
@property(nonatomic) CGFloat flatness; 
// 决定使用even-odd或者non-zero规则
@property(nonatomic) BOOL usesEvenOddFillRule;  
  • 方法
// 反方向绘制path
- (UIBezierPath *)bezierPathByReversingPath;
// 设置画笔起始点
- (void)moveToPoint:(CGPoint)point;
// 从当前点到指定点绘制直线
- (void)addLineToPoint:(CGPoint)point;
// 添加弧线
- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise NS_AVAILABLE_IOS(4_0);
/* center弧线圆心坐标 radius弧线半径 startAngle弧线起始角度 endAngle弧线结束角度 clockwise是否顺时针绘制 */
// 添加贝塞尔曲线
- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;
/* endPoint终点 controlPoint控制点 */
- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2;
/* endPoint终点 controlPoint1、controlPoint2控制点 */
// 移除所有的点,删除所有的subPath
- (void)removeAllPoints;
// 将bezierPath添加到当前path
- (void)appendPath:(UIBezierPath *)bezierPath;
// 填充
- (void)fill;
// 路径绘制
- (void)stroke;
// 在这以后的图形绘制超出当前路径范围则不可见
- (void)addClip;

3. UIBezierPath的简单使用

基本步骤:
1.获取上下文 -> 2.描述路径 -> 3.添加路径到上下文 -> 4.把上下文的内容渲染到View的layer上

  • UIBezierPath绘制直线
  • 无论是开启上下文还是获取上下文都是以以UIGraphics开头
  • 图形上下文(Graphics Context):是一个CGContextRef类型的数据
  • 图形上下文的作用:
    保存绘图信息、绘图状态
    决定绘制的输出目标(绘制到什么地方去?)
    (输出目标可以是PDF文件、Bitmap或者显示器的窗口上)
  • 相同的一套绘图序列,指定不同的Graphics Context,就可将相同的图像绘制到不同的目标上
  • Quartz2D提供了以下几种类型的Graphics Context:
    Bitmap Graphics Context
    PDF Graphics Context
    Window Graphics Context
    Layer Graphics Context
    Printer Graphics Context
实例展示:

/**
 作用:专门用来绘图
 什么时候调用:当View显示时调用
 rect :代表当前控件的bounds
 */
- (void)drawRect:(CGRect)rect {
    NSLog(@"%s",__func__) ;
    //在此方法中内部会自动创建一个跟View相关联的上下文
    //可以直接获取
    NSLog(@"%@",NSStringFromCGRect(rect)) ;
    //小技巧:无论是开启上下文还是获取上下文都是以UIGraphics开头
    //1.获取当前跟View相关联的上下文(画板 <- 比喻)
    CGContextRef ctx = UIGraphicsGetCurrentContext() ;
    //2.描述路径(画笔)
    UIBezierPath * path = [UIBezierPath bezierPath] ;
    //2.1设置起点(画笔的落点) 坐标的原点是以当前绘制View的左上角为(0,0)原点来参考.
    [path moveToPoint:CGPointMake(50, 50)] ;
    //2.2添加一根线到某个点,终点(画笔的收点)
    [path addLineToPoint:CGPointMake(50, 250)] ;
    
    //一个路径可以描述多条线
    //再画另外一个点
    [path moveToPoint:CGPointMake(0, 0)] ; //起点
    [path addLineToPoint:CGPointMake(50, 30)] ;//终点
    
    //上一个路径的终点直接设为上一个路径的起点(x相同)
    [path addLineToPoint:CGPointMake(50,70)]; //终点x和起点x相同
    
    [path addLineToPoint:CGPointMake(30,70)]; //终点y和起点y相同
    
    //设置上下文的状态
    CGContextSetLineWidth(ctx, 5) ; // 设置线条的宽度
    //设置上下文的链接样式
    /*
     typedef CF_ENUM(int32_t, CGLineJoin) {
     kCGLineJoinMiter,默认样式
     kCGLineJoinRound,圆弧样式
     kCGLineJoinBevel,切去尖角样式
     };
     */
    CGContextSetLineJoin(ctx, kCGLineJoinRound) ;
    
    //设置顶角样式
    /*
     typedef CF_ENUM(int32_t, CGLineCap) {
     kCGLineCapButt,
     kCGLineCapRound,
     kCGLineCapSquare
     };
     */
    CGContextSetLineCap(ctx, kCGLineCapButt) ;
    
    //设置线的颜色  setStroke setFill 直接使用set,会自动匹配渲染的模式
    //[[UIColor redColor] setStroke] ;//描边
    //[[UIColor redColor] setFill] ; //填充
    [[UIColor redColor] set] ;
    
    //3.把路径添加到上下文
    CGContextAddPath(ctx, path.CGPath) ;
    
    //4.把上下文的内容渲染到View相关联的layer上(将画板添加到View)
    //渲染的上市有两种:
    //1.描边:stroke
    //2.填充:fill
    CGContextStrokePath(ctx) ;
    
}
效果展示
  • UIBezierPath绘制曲线

画曲线,一般是一条直线,然后定义几个控制点,使直线变弯曲。

二次曲线函数

三次曲线函数
- (void)drawRect:(CGRect)rect {
    //1.获取上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext() ;
    //2.描述路径
    UIBezierPath *path = [UIBezierPath bezierPath] ;
    //画笔起点
    [path moveToPoint:CGPointMake(20, 30)] ;
    //添加一条曲线到某个点上
    //参数一:终点
    //参数二:控制点
    [path addQuadCurveToPoint:CGPointMake(200, 30) controlPoint:CGPointMake(100, 100)] ;
    //3.把路径添加到上下文
    CGContextAddPath(ctx, path.CGPath) ;
    //4.把上下文的内容渲染到View的layer
    CGContextStrokePath(ctx) ;
}
效果
  • UIBezierPath绘制矩形
    bezierPathWithRect:描述矩形
    cornerRadius:可以理解成矩形最上方一个点到矩形左边顶点的距离。
- (void)drawRect:(CGRect)rect {
    //获取上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext() ;
    //描述矩形路径
    /*
     bezierPathWithRect:描述矩形
     参数:矩形的w尺寸大小
     */
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(50, 50, 200, 100)] ;
    
    /*
     bezierPathWithRect:描述圆角矩形
     参数一:矩形的尺寸大小
     参数二:矩形的圆角半径
     */
    path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(50, 50, 200, 100) cornerRadius:15.f] ;
    
    //设置上下文的状态得要在渲染之前设置
    [[UIColor redColor] set] ;
    
    //把路径添加到上下文
    CGContextAddPath(ctx, path.CGPath) ;
    //把上下文的内容渲染到View的layer上
//    CGContextStrokePath(ctx) ;//描边
    CGContextFillPath(ctx) ;
}
矩形效果
  • UIBezierPath绘制圆

描述圆的画法有很多
bezierPathWithOvalInRect:绘制圆

- (void)drawRect:(CGRect)rect {
    //1.获取上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext() ;
    //2.描述绘画路径 bezierPathWithOvalInRect:绘制圆
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 50, 100, 200)] ;
    //当宽度和高度一样的时候就是正圆了
    path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 50, 100, 100)] ;
    //3.把路径添加到上下文
    CGContextAddPath(ctx, path.CGPath) ;
    
    //设置上下文
    [[UIColor magentaColor] set] ;
    
    //4.把上下文的内容渲染到View的layer上
    CGContextFillPath(ctx) ;
}
  • 补充:快捷方法 - 其他图形也适用

- (void)drawRect:(CGRect)rect {
    //2.描述绘画路径 bezierPathWithOvalInRect:绘制圆
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 50, 100, 200)] ;
    [[UIColor redColor]set] ;
    [path stroke] ;
//    [path fill] ;
}
效果
  • UIBezierPath绘制扇形和圆弧
- (void)drawRect:(CGRect)rect {    
    /**
     绘制圆弧
     Center: 中心点坐标
     radius:弧所在的半径
     startAngle:开始的角度 ; 0度是圆的最右侧,向上的度数为负的,向下的度数为正的
     endAngle:结束的角度
     clockwise:是顺时针还是逆时针  默认:顺时针
     */
    
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 100) radius:50 startAngle:0 endAngle:M_PI_2 clockwise:YES] ;
    //添加一根线到圆心,然后关闭路径就成了扇形
    [path addLineToPoint:CGPointMake(100, 100)];
    //关闭路径
    [path closePath] ;
    [path stroke] ;
    [path fill] ;//当使用填充时,会自动关闭路径
    
}
效果
实例-绘制下载进度条
目录结构

ProgressView.h

#import 

NS_ASSUME_NONNULL_BEGIN

@interface ProgressView : UIView
@property(nonatomic,assign)CGFloat value ;

@end

NS_ASSUME_NONNULL_END

ProgressView.m

#import "ProgressView.h"

@implementation ProgressView
-(void)setValue:(CGFloat)value{
    _value = value ;
    //这样系统不会执行drawRect方法
    //[self drawRect:self.bounds] ;
    //当系统调用drawRect方法的时候,在drawRect会自动创建跟View相关的上下文
    //手动调用是没有效果的
    //通知系统,执行drawRect方法
    //setNeedsDisplay:重新绘制
    [self setNeedsDisplay] ;
}

- (void)drawRect:(CGRect)rect {
    NSLog(@"%@",NSStringFromCGRect(rect));
    // Drawing code
    //1.获取view相关联的上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext() ;
    //2.描述路径
    CGPoint center = CGPointMake(rect.size.width * 0.5, rect.size.height * 0.5) ;
    NSLog(@"%@",NSStringFromCGPoint(center)) ;
    CGFloat radius = rect.size.height * 0.5 -20 ;
    NSLog(@"%f",radius) ;
    CGFloat startAngle = -M_PI_2 ;
    CGFloat endAngle = startAngle + self.value * M_PI * 2 ;
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES] ;
    
    [[UIColor blueColor] set] ;
    
    //关闭路径
    [path addLineToPoint:center];
    
    //3.把路径添加到上下文
    CGContextAddPath(ctx, path.CGPath) ;
    //4.把上下文的l内容渲染到View上
//    CGContextStrokePath(ctx) ;
    CGContextFillPath(ctx) ;
    
}

@end

ViewController.m

#import "ViewController.h"
#import "ProgressView.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet ProgressView *progressView;

@property (weak, nonatomic) IBOutlet UILabel *progressLabel;
@property (weak, nonatomic) IBOutlet UISlider *progressSlider;

@end

@implementation ViewController
- (IBAction)progressValueChange:(UISlider *)sender {
    self.progressView.value = sender.value ;
    NSString *strValue = [NSString stringWithFormat:@"%.2f%%",sender.value * 100] ;
    self.progressLabel.text = strValue ;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
}
@end
屏幕快照 2019-01-22 11.48.38.png
相关图片参考网络

这样简单的使用Quartz2D就完成了,希望能够一起进步,共勉❤️。

求职广告:本人实习生,现在急需一份工作,杭州南京合肥的都可以。对我感兴趣的可以私聊我 0.0。谢谢~~~

你可能感兴趣的:(iOS - 用UIBezierPath介绍和实现简单的扇形下载进度条)