iOS - 用UIBezierPath介绍和实现简单的扇形下载进
2019-01-21 本文已影响0人
ShIwEn9
所写知识点来自《小码哥视频教程》
吐槽:实习生太苦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 <UIKit/UIKit.h>
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。谢谢~~~