转载自:http://www.jianshu.com/p/f9f05e12ddd3#
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICcpJHdz9CX05WZpJ3bt8Gd1F2LcJjcn9WTldWYtl2Pml2ZuYzNwYGOkFWOyITNlZGZ2ATL1IzN4QjNx8CXzV2Zh1WafRWYvxGc19CXvlmL1h2cuFWaq5ycldWYtlWLkF2bsBXdvw1LcpDc0RHaiojIsJye.gif)
demo中有苹果婊
什么是Quartz2D?二维的绘图引擎
什么是二维?平面
什么是引擎?经包装的函数库,方便开发者使用。也就是说苹果帮我们封装了一套绘图的函数库
同时支持iOS和Mac系统什么意思?用Quartz2D写的同一份代码,既可以运行在iphone上又可以运行在mac上,可以跨平台开发。
开发中比较常用的是截屏/裁剪/自定义UI控件。
Quartz2D在iOS开发中的价值就是自定义UI控件。
使用图形上下文画图,要遵循一下四个步骤
1.获取图像上下文
2.创建路径
3.将路径添加到图形上下文(add)
4.渲染图像上下文(fill,stroke)
一.绘制一条直线的方法
1.直接使用图形上下文画图
//只有在drawRect方法中才能拿到图形上下文,才可以画图
- (void)drawRect:(CGRect)rect {
//1.获取图形上下文,目前我们现在使用的都是UIGraphics开头,CoreGraphics,项目的简称CG
CGContextRef ctx = UIGraphicsGetCurrentContext();
//2.描述路径
//2.1 创建路径
CGContextMoveToPoint(ctx, , );
//2.2 添加线到一个点
CGContextAddLineToPoint(ctx, , );
//3.完成路线
CGContextStrokePath(ctx);
}
2.使用图形上下文+CGPathRef画线
- (void)drawRect:(CGRect)rect {
//1.获取图形上下文,目前我们现在使用的都是UIGraphics开头,CoreGraphics,项目的简称CG
CGContextRef ctx = UIGraphicsGetCurrentContext();
//2.使用path来画线
CGMutablePathRef path = CGPathCreateMutable();
//3.添加点
CGPathMoveToPoint(path, NULL, , );
CGPathAddLineToPoint(path, NULL, , );
//4.将path添加到图像上下文上
CGContextAddPath(ctx, path);
//5.渲染上下文
CGContextStrokePath(ctx);
}
3.贝塞尔曲线
实际上方法都是一样的,只是添加没有获取图形上下文,在底层,系统已经给封装了
- (void)drawRect:(CGRect)rect {
//1.创建路径
UIBezierPath *path = [UIBezierPath bezierPath];
//2.画线
[path moveToPoint:CGPointMake(, )];
[path addLineToPoint:CGPointMake(, )];
//3.渲染
[path stroke];
}
4.图形上下文+贝塞尔曲线
- (void)drawRect:(CGRect)rect {
//使用贝塞尔曲线和图形上下文画图
CGContextRef ctx = UIGraphicsGetCurrentContext();
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointZero];
[path addLineToPoint:CGPointMake(, )];
//这个是C语言的写法,必须使用path.CGPath,否则无效
CGContextAddPath(ctx, path.CGPath);
CGContextStrokePath(ctx);
}
1.用四种方法,说明绘制图形有很多种方法
2.底层的是
,绘制图形实际上就是一直设置
2.CGMutablePathRef
3.
path
c语言的方法,
CGContextMoveToPoint(实际上底层已经给我们分装了CGPat,但只有一个)
UIBezierPath
抽象出的面向对象的类
4.不管哪种,思路都是一样的
5.个人推荐的是使用1,2,3,不推荐4,两个东西杂糅,不太喜欢
二.画两个链接的线,并且设置属性
两条相交的线
//只有在drawRect方法中才能拿到图形上下文,才可以画图
- (void)drawRect:(CGRect)rect {
//1.获取图形上文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//2.将绘制路径,并且将其添加到图形上下文
CGContextMoveToPoint(ctx,, );
CGContextAddLineToPoint(ctx, , );
//添加另一根线
CGContextAddLineToPoint(ctx, , );
//设置颜色
[[UIColor greenColor] set];
//设置线的宽度
CGContextSetLineWidth(ctx, );
//设置链接处的链接类型
CGContextSetLineJoin(ctx, kCGLineJoinMiter);
//设置线的头部样式
CGContextSetLineCap(ctx, kCGLineCapButt);
//渲染
CGContextStrokePath(ctx);
}
画出了两条有链接的线,其中设置颜色的时候,是区分设置线的颜色,和设置图片的颜色的,可以设置他们各自的属性(但是经常设置错误),懒得去区分并且保证不会设置错误,建议设置就不用考虑实线还是填充图形了。还有,
[[UIColor greenColor] set]
是设置连接处的样式,是枚举,
CGContextSetLineJoin
CGContextSetLineCap
是设置线的顶部的样式,也是枚举。
注意:设置各种属性的时候,一定要记住在渲染之前,否则无效
三.绘制两条不相交的线,并且设置各自属性
两条不相交的线,设置他们的属性
- (void)drawRect:(CGRect)rect {
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(, )];
[path addLineToPoint:CGPointMake(, )];
[path setLineWidth:];
[[UIColor redColor] set];
[path stroke];
//设置第2个线段
UIBezierPath *path2 = [UIBezierPath bezierPath];
[path2 moveToPoint:CGPointMake(, )];
[path2 addLineToPoint:CGPointMake(, )];
[path2 setLineWidth:];
[[UIColor greenColor] set];
[path2 setLineCapStyle:kCGLineCapRound];
[path2 stroke];
}
使用贝塞尔曲线画图的好处在于,1.每一个贝塞尔底层都有一个图形上线文,如果是用画图,实际上就是一个图形上下文,不好去控制,所以建议没多条线可以使用贝塞尔曲线或者说使用底层的
CGContextMoveToPoint
画线,比较靠谱
CGMutablePathRef
四.绘制曲线
曲线
- (void)drawRect:(CGRect)rect {
//绘制一条曲线
CGContextRef ctx = UIGraphicsGetCurrentContext();
//设置起点
CGContextMoveToPoint(ctx, , );
/**
* 添加曲线的五个参数
*
* @param c#> 图形上下文
* @param cpx#> 将来要突出的x值
* @param cpy#> 要突出的y值
* @param x#> 曲线结束时的x
* @param y#> 曲线结束时的y
*/
CGContextAddQuadCurveToPoint(ctx, , , , );
//设置颜色
[[UIColor redColor] set];
//渲染图层
CGContextStrokePath(ctx);
}
五.绘制带有圆角边框的正方形
圆角的矩形们
- (void)drawRect:(CGRect)rect {
//绘制一个圆角矩形
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(, , , ) cornerRadius:];
[[UIColor orangeColor] set];
[path stroke];
//绘制一个圆角矩形
UIBezierPath *path2 = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(, , , ) cornerRadius:];
[[UIColor greenColor] set];
[path2 fill];
//绘制一个原形(不规范的绘制)
UIBezierPath *path3 = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(, , , ) cornerRadius:];
[[UIColor cyanColor] set];
[path3 fill];
}
- 1.
设置边框的颜色,
stroke
填充内部的颜色
fill
- 2.
并不是随意使用的,必须是封闭的图形。
fill
- 3.可以通过设置圆角是正方形的高度,生成一个原形,但不是最规范绘制原形的方法,不过也可以使用。
六.绘制一个弧度曲线
弧度曲线
- (void)drawRect:(CGRect)rect {
/**
* 绘制弧度曲线
*
* @param ArcCenter 曲线中心
* @param radius 半径
* @param startAngle 开始的弧度
* @param endAngle 接收的弧度
* @param clockwise YES顺时针,NO逆时针
*/
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(, ) radius: startAngle: endAngle:M_PI clockwise:YES];
[[UIColor greenColor] set];
[path stroke];
//结束角度如果是M_PI *2 就是一个圆
UIBezierPath *path1 = [UIBezierPath bezierPathWithArcCenter:CGPointMake(, ) radius: startAngle: endAngle:M_PI * clockwise:YES];
[[UIColor purpleColor] set];
[path1 fill];
//绘制115度角的圆弧
UIBezierPath *path2 = [UIBezierPath bezierPathWithArcCenter:CGPointMake(, ) radius: startAngle: endAngle:/*(M_PI *) clockwise:NO];
[[UIColor orangeColor] set];
[path2 stroke];
}
1.是180度.
M_PI
M_PI_2
是90°
2.这里的角度都是弧度制度,如果我们需要15°,可以用
15°/180°*π
得到。
3.
这个是顺时针,如果穿1,就是
clockwise
,穿0,是
顺时针
逆时针
七.绘制一个一个扇形
扇形
- (void)drawRect:(CGRect)rect {
//1.获取图形上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//绘制曲线
CGFloat centerX = ;
CGFloat centerY = ;
CGFloat radius = ;
//添加一根线·
CGContextMoveToPoint(ctx, centerX, centerY);
CGContextAddArc(ctx, centerX, centerY, radius, M_PI, M_PI_2, NO);
//关闭线段
CGContextClosePath(ctx);
//渲染
CGContextStrokePath(ctx);
}
1.线添加一个点2.添加一个圆弧
CGContextMoveToPoint
3.闭合绘图
CGContextAddArc
4.给路径设置颜色
CGContextClosePath
,或者给图形内部设置颜色
CGContextStrokePath
5.使用贝塞尔曲线,也要设置闭合路径
CGContextFillPath
CGContextClosePath
八.简单下载进度的demo
demo下载进度
- (void)setProgressValue:(CGFloat)progressValue{
_progressValue = progressValue;
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {
CGFloat radius = self.frame.size.height * - ;
CGFloat endA = self.progressValue*M_PI* - M_PI_2;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(radius+,radius+) radius:radius- startAngle:-M_PI_2 endAngle:endA clockwise:YES];
path.lineWidth = ;
path.lineCapStyle = kCGLineCapRound;
[[UIColor redColor] set];
[path stroke];
}
demo上传github,DEMO链接,上边是重要的代码
2.在
的
UIBezierPath
bezierPathWithArcCenter:radius:startAngle:endAngle:clockwise:
中clockwise: == 1表示顺时针
3.
CGContextAddArc(ctx, centerX, centerY, radius, M_PI, M_PI_2, NO)
,clockwise == 0表示顺时针
4.想调用这个方法
,必须要使用
drawRect:(CGRect)rect
,其他的无效。
setNeedsDisplay
九.饼状图
饼状图
@interface WXView ()
@property(nonatomic,strong)NSArray *nums;
@property(assign,nonatomic)NSInteger total;
@end
@implementation WXView
-(NSInteger )total{
if (_total == ) {
for (int j = ; j < self.nums.count; j++) {
_total += [self.nums[j] integerValue];
}
}
return _total;
}
-(NSArray *)nums{
if (!_nums) {
_nums = @[@,@,@,@,@,@,@];
}
return _nums;
}
//只有在drawRect方法中才能拿到图形上下文,才可以画图
- (void)drawRect:(CGRect)rect {
//绘制一个饼状图
CGFloat centX = ;
CGFloat centY = ;
CGPoint center = CGPointMake(centX, centY);
CGFloat radius = ;
CGFloat startA = ;
CGFloat endA = ;
for (int i = ; i < self.nums.count; i++) {
NSNumber *data = self.nums[i];
startA = endA;
endA = startA + [data floatValue]/self.total * * M_PI;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];
[path addLineToPoint:center];
CGFloat randRed = arc4random_uniform()/;
CGFloat randGreen = arc4random_uniform()/;
CGFloat randBlue = arc4random_uniform()/;
UIColor *randomColor = [UIColor colorWithRed:randRed green:randGreen blue:randBlue alpha:];
[randomColor set];
[path fill];
}
}
十.柱状图
柱状图
-(NSArray *)nums{
if (!_nums) {
_nums = @[@,@,@,@,@,@];
}
return _nums;
}
//只有在drawRect方法中才能拿到图形上下文,才可以画图
- (void)drawRect:(CGRect)rect {
//1.获取图形上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//2.绘制图像
CGFloat margin = ;
CGFloat width = (rect.size.width - (self.nums.count + )*margin)/self.nums.count;
for (int i = ; i < self.nums.count; i++) {
CGFloat x = margin + (width + margin)*i;
CGFloat num = [self.nums[i] floatValue]/;
CGFloat y = rect.size.height * (-num);
CGFloat height = rect.size.height*num;
CGRect rectA = CGRectMake(x, y, width, height);
CGContextAddRect(ctx,rectA);
[[self randomColor] set];
CGContextFillPath(ctx);
}
}
十一.绘制图片
绘制文字和图片的时候,是不用去获取图像上下文的
平铺
固定位置画图
默认固定大小
- (void)drawRect:(CGRect)rect {
//剪切图片,超出的图片位置都要剪切掉!必须要在绘制之前写,否则无效
// UIRectClip(CGRectMake(0, 0, 100, 50));
UIImage *image = [UIImage imageNamed:@"1"];
//(渲染)默认绘制的大小就是图片的大小,也可以自己设置一下
//在什么范围下(原图大小)
[image drawInRect:rect];
//在哪个位置开始画
[image drawAtPoint:CGPointMake(, )];
//平铺
[image drawAsPatternInRect:rect];
}
十二.绘制富文本
苹果富文本
- (void)drawRect:(CGRect)rect {
NSString *str = @"每个人都\n应该喜欢敲代\n码,只有这样,\n科技才能改变生活";
//设置文字的属性
NSMutableDictionary * paras = [NSMutableDictionary dictionary];
paras[NSFontAttributeName] = [UIFont systemFontOfSize:];
paras[NSForegroundColorAttributeName] = [UIColor redColor];
paras[NSStrokeColorAttributeName] = [UIColor orangeColor];
paras[NSStrokeWidthAttributeName] = @;
//创建阴影对象
NSShadow *shodow = [[NSShadow alloc] init];
shodow.shadowColor = [UIColor blueColor];
shodow.shadowOffset = CGSizeMake(, );
shodow.shadowBlurRadius = ;
//苹果的富文本就是这样搞出来的
paras[NSShadowAttributeName] = shodow;
[str drawAtPoint:CGPointZero withAttributes:paras];
}
十三.自定义一个ImageView
WXImageView继承自UIView
WXImageView.h
文件中定义的属性和接口和
UIImageView
一样,继承自
UIView
- (id)initWithImage:(UIImage *)image;
@property(nonatomic,strong)UIImage *image;
WXImageView.m
中方法的具体实现
- (id)initWithImage:(UIImage *)image{
if (self = [super initWithFrame:CGRectMake(, , image.size.width, image.size.height)]) {
_image = image;
}
return self;
}
- (void)setImage:(UIImage *)image{
_image = image;
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {
[self.image drawInRect:rect];
}
在vc中调用
WXImageView *imageV = [[WXImageView alloc] initWithImage:[UIImage imageNamed:@"1"]];
imageV.center = CGPointMake(, );
[self.view addSubview:imageV];
即使在xib使用,我发现和 UIImageView
的结果都是一样的
十四.雪花飘动
雪花动画
//只有在drawRect方法中才能拿到图形上下文,才可以画图
- (void)drawRect:(CGRect)rect {
//设置下雪的动画
UIImage *image = [UIImage imageNamed:@"snow"];
_snowY += ;
[image drawAtPoint:CGPointMake(, _snowY)];
if (_snowY >= rect.size.height) {
_snowY = ;
}
}
// 如果在绘图的时候需要用到定时器,通常使用CADisplayLink
// NSTimer很少用于绘图,因为调度优先级比较低,并不会准时调用
- (void)awakeFromNib
{
// 创建定时器
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(timeChange)];
// 添加主运行循环
[link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
// CADisplayLink:每次屏幕刷新的时候就会调用,屏幕一般一秒刷新60次
- (void)timeChange{
[self setNeedsDisplay];
}
1.本质就是调用
drawRect
方法,一直刷新雪花的y值
2.每一次调用
,都创建大量的对象,有人说可能性能不好,不过你可能多虑了,没吃都是在内存加载,不会创建新的
drawRect
UIImage
十五.图形上下文栈
我自己详细的介绍了一下,上下文栈,可以看一下~
图形上下文详解
十六.图形上下文矩阵
到底是个啥?
就是图形上下文画出的东西永远是方方正正的,你要是想画个偏离的矩形,按照过去的方法画不出来,只能使用矩阵的方式。分别有偏移,缩放,旋转
正常尺寸的椭圆
x,y各自平移10px后的椭圆
旋转后的椭圆
缩放后的椭圆
- (void)drawRect:(CGRect)rect {
//图形上下文矩阵
//1.画一个椭圆
CGContextRef ctx = UIGraphicsGetCurrentContext();
// CGContextAddEllipseInRect(ctx, CGRectMake(100, 100, 100, 100));
CGPathRef path = CGPathCreateWithEllipseInRect(CGRectMake(, , , ),nil);
[[UIColor redColor] set];
//1.偏移
// CGContextTranslateCTM(ctx, 10, 10);
//2.旋转
// CGContextRotateCTM(ctx, M_PI_4);
//3.缩放
CGContextScaleCTM(ctx, , );
CGContextAddPath(ctx, path);
CGContextFillPath(ctx);
}
1.绘制变化的图形的步骤
- 先绘制path
- 设置图形上下文矩阵
- 将path添加到图形上下文(这一步很重要,一定按照步骤来)
- 渲染
2.绘图的时候,我们要使用底层的 CGPathRef,或者贝塞尔,然后 CGContextRef+path的方式。
如不这样,我注释的
CGContextAddEllipseInRect(ctx, CGRectMake(100, 100, 100, 100))
方法,这个方法中已经先去添加了path到图形上下文,即使我们在去添加图形上下文矩阵,也是无效
3.可以和图形上下文栈一起使用,给特定的一些图案设置一些属性,还有一些不会受到影响
本文特别感谢小马哥教育,参考了他们的视频,做的一个总结。文章的demo已经更新到了github,如有需要,可以参考
文/王鑫20111(简书作者)
原文链接:http://www.jianshu.com/p/f9f05e12ddd3#
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。