本篇文章是学习传智播客iOS开发课程的整理笔记。
1> Quartz2D简介
* PPT简介
1.什么是Quartz2D?二维的绘图引擎
2.什么是二维?平面
3.什么是引擎?经包装的函数库,方便开发者使用。也就是说苹果帮我们封装了一套绘图的函数库
4.同时支持iOS和Mac系统什么意思?用Quartz2D写的同一份代码,既可以运行在iphone上又可以运行在mac上,可以跨平台开发。
5.开发中比较常用的是截屏/裁剪/自定义UI控件。
6. Quartz2D在iOS开发中的价值就是自定义UI控件。
7.图形上下文的数据类型和作用。
* CGContextRef类型的数据;
* 作用:
7.1.保存绘图信息、绘图状态
7.2.决定绘制的输出目标(绘制到什么地方去?)输出目标可以是PDF文件、Bitmap或者显示器的窗口上)
7.3.相同的一套绘图序列,指定不同的Graphics Context,就可将相同的图像绘制到不同的目标上
8.有多少种上下文。
1) Bitmap Graphics Context
2) PDF Graphics Context
3) Window Graphics Context
4) Layer Graphics Context
5) Printer Graphics Context
9.自定义控件的步骤。
* 如何利用Quartz2D自定义UI控件?
* 如何利用Quartz2D绘制东西到view上?
首先,得有图形上下文,因为它能保存绘图信息,并且决定着绘制到什么地方去
其次,那个图形上下文必须跟view相关联,才能将内容绘制到view上面
* 自定义UI控件的步骤
1.新建一个类,继承自UIView
2.实现 - (void)drawRect:(CGRect)rect方法,然后在这个方法中,可以:
1) 取得跟当前view相关联的图形上下文
2) 绘制相应的图形内容,绘制时产生的线条称为路径。路径由一个或多个直线段或曲线段组成。
3) 利用图形上下文将绘制的所有内容渲染显示到view上面
* 也可以:
利用UIKit封装的绘图函数直接绘图
10.用Quartz2D绘图的代码步骤:
1>获得图形上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
2>拼接路径(下面代码是搞一条线段)
CGContextMoveToPoint(ctx,10,10);
CGContextAddLineToPoint(ctx,100,100);
3>绘制路径
CGContextStrokePath(ctx);// CGContextFillPath(ctx);
11.常用拼接路径的函数:
1>新建一个起点
void CGContextMoveToPoint(CGContextRef c, CGFloat x, CGFloat y)
2>添加新的线段到某个点
void CGContextAddLineToPoint(CGContextRef c, CGFloat x, CGFloat y)
3>添加一个矩形
void CGContextAddRect(CGContextRef c, CGRect rect)
4>添加一个椭圆
void CGContextAddEllipseInRect(CGContextRef context, CGRect rect)
5>添加一个圆弧
void CGContextAddArc(CGContextRef c, CGFloat x, CGFloat y,CGFloat radius, CGFloat startAngle, CGFloat endAngle,int clockwise)
12.常用绘制路径的函数:
1> Mode参数决定绘制的模式
void CGContextDrawPath(CGContextRef c, CGPathDrawingMode mode)
2>绘制空心路径
void CGContextStrokePath(CGContextRef c)
3>绘制实心路径
void CGContextFillPath(CGContextRef c)
4>提示:一般以CGContextDraw、CGContextStroke、CGContextFill开头的函数,都是用来绘制路径的
13.为什么要实现drawRect:方法,因为只有在drawRect:方法中才能获取到上下文。
14.在drawRect:方法中取得上下文后,就可以绘制东西到view上
15.View内部有个layer(图层)属性,drawRect:方法中取得的是一个Layer Graphics Context,因此,绘制的东西其实是绘制到view的layer上去了
16. View之所以能显示东西,完全是因为它内部的layer
17. drawRect:方法在什么时候被调用?
1)当view第一次显示到屏幕上时(被加到UIWindow上显示出来)
2)调用view的setNeedsDisplay或者setNeedsDisplayInRect:时
18. Quartz2D数据类型和函数基本都以CG作为前缀,比如:CGContextRef、CGPathRef、CGContextStrokePath(ctx)……
18.
2>Quartz2D绘图演练
在哪画? storyboard拖一个view,在这个view里面画一些东西。
自定义view:需要绘图,就必须重写drawRect:方法
* HMLineView:绘制线段必须画图分析
drawRect:方法自动生成,意味着什么?这个方法很重要。
1>什么时候调用:视图要显示的时候,才会调用,viewDidLoad之后才会调用,因为那时候还没显示视图。
2>作用:用来绘图
3> drawRect的参数rect相当于self.bounds
* 画一条线
1>获取图形上下文
CG:表示这个类在CoreGraphics框架里 Ref:引用
目前学的上下文都跟UIGraphics有关,想获取图形上下文,首先敲UIGraphics。
<span style="white-space:pre"> </span>CGContextRef ctx = UIGraphicsGetCurrentContext();
2>拼接路径(设置绘图信息):一般开发中用贝塞尔路径,里面封装了很多东西,可以帮我画一些基本的线段,矩形,圆等等。
创建贝塞尔路径:
UIBezierPath *path = [UIBezierPath bezierPath];
起点:moveToPoint
[path moveToPoint:CGPointMake(10, 10)];
终点:addLineToPoint
[path addLineToPoint:CGPointMake(125, 125)];
3>把路径添加到上下文
CGPath转换:UIKit框架转成CoreGraphics直接CGPath就能转
CGContextAddPath(ctx, path.CGPath);
4>把上下文渲染到视图,图形上下文本身不具备显示功能。
PPT画图分析为什么要这样做?首先获取图形上下文,然后描述路径,把路径添加到上下文,渲染到视图,图形上下文相当于一个内存缓存区,在内存里面操作是最快的,比直接在界面操作快多了。
CGContextStrokePath(ctx);
* 再添加一根线(两个线相连接)
直接addLineToPoint,因为路径是拼接的,默认下一条线的起点是上一条线的终点。
- (void)drawRect:(CGRect)rect
{
// 1.获取上下文
// CGContextRef CG CoreGraphics Ref 引用
// 目前学的上下文都跟UIGraphics有关,以后想直接获取上下文,直接敲一个UIGraphics
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.设置绘图信息(拼接路径)
UIBezierPath *path = [UIBezierPath bezierPath];
// 两条线(路径):A -> B, B -> C
// 设置起点(A点)
[path moveToPoint:CGPointMake(10, 10)];
// 添加一条线到某个点(B点)
[path addLineToPoint:CGPointMake(125, 125)];
// 添加另一线到某个点(C点)
[path addLineToPoint:CGPointMake(240, 10)];
// 3.把路径添加到上下文
// 直接把UIKit的路径转换成CoreGraphics,CG开头就能转
CGContextAddPath(ctx, path.CGPath);
// 4.把上下文渲染到视图
// Stroke描边
CGContextStrokePath(ctx);
}
* 画两跟不连接的线
1>第二次画的时候,重新设置起点,然后画线。一个路径可以包含多条线段。
<pre name="code" class="objc">// 画两条不相连的线:A -> B,C -> D
// 方法一(不推荐):创建一个路径,但是有两个起点
- (void)drawRect:(CGRect)rect
{
// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.拼接路径
UIBezierPath *path = [UIBezierPath bezierPath];
// 设置起点(A点)
[path moveToPoint:CGPointMake(10, 125)];
// 添加一条线到某个点(B点)
[path addLineToPoint:CGPointMake(230, 125)];
// 设置起点(C点)
[path moveToPoint:CGPointMake(10, 10)];
// 添加一条线到某个点(D点)
[path addLineToPoint:CGPointMake(125, 100)];
// 3.把路径添加到上下文
CGContextAddPath(ctx, path.CGPath);
// 4.渲染上下文到视图
CGContextStrokePath(ctx);
}
2>新创建一个路径,添加到上下文。开发中建议使用这种,比较容易控制每根线。
// 画两条不相连的线:A -> B,C -> D
// 方法二(推荐用):创建两个路径
- (void)drawRect:(CGRect)rect
{
// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.拼接路径
UIBezierPath *path = [UIBezierPath bezierPath];
// 设置起点
[path moveToPoint:CGPointMake(10, 125)];
// 添加一条线到某个点
[path addLineToPoint:CGPointMake(230, 125)];
UIBezierPath *path1 = [UIBezierPath bezierPath];
[path1 moveToPoint:CGPointMake(10, 10)];
[path1 addLineToPoint:CGPointMake(125, 100)];
// 3.把路径添加到上下文
CGContextAddPath(ctx, path.CGPath);
CGContextAddPath(ctx, path1.CGPath);
// 设置绘图状态
// 设置线宽
CGContextSetLineWidth(ctx, 10);
// 设置圆角
CGContextSetLineCap(ctx, kCGLineCapRound);
// 设置颜色(不推荐用此方法设置颜色)
// CGContextSetRGBStrokeColor(ctx, 1, 0, 0, 1);
// 设置颜色(推荐用此方法设置颜色)
[[UIColor redColor] set];
// 4.渲染上下文到视图
CGContextStrokePath(ctx);
}
* 设置绘图状态
线段怎么加粗。
绘图状态调用顺序:只要在渲染之前就好了,在渲染的时候才会去看绘图的最终状态。
// 设置绘图状态
// 设置线宽
CGContextSetLineWidth(ctx, 10);
// 设置圆角
CGContextSetLineCap(ctx, kCGLineCapRound);
// 设置颜色(不推荐用此方法设置颜色)
// CGContextSetRGBStrokeColor(ctx, 1, 0, 0, 1);
// 设置颜色(推荐用此方法设置颜色)
[[UIColor redColor] set];
// 4.渲染上下文到视图
CGContextStrokePath(ctx);
(运行效果见上图-红色加粗)
* 画曲线
PPT分析:3个点,起点,终点,控制点。
- (void)drawRect:(CGRect)rect{
// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.拼接路径
UIBezierPath *path = [UIBezierPath bezierPath]; // 创建路径
CGPoint startP = CGPointMake(10, 125); // 起点
CGPoint endP = CGPointMake(240, 125); // 终点
CGPoint controlP = CGPointMake(125, 0); // 拐点
// 设置起点
[path moveToPoint:startP];
// 设置终点和拐点
[path addQuadCurveToPoint:endP controlPoint:controlP];
// 3.把路径添加到上下文
CGContextAddPath(ctx, path.CGPath);
// 4.渲染上下文到视图
CGContextStrokePath(ctx);
}
* HMShapeView:绘制图形
* triangle三角形(画图分析)
* 关闭路径closePath:从路径的终点连接到起点
* 填充路径CGContextFillPath:有了封闭的路径就能填充。
* 设置填充颜色 [[UIColor blueColor] setFill];
* 设置描边颜色 [[UIColor redColor] setStroke];
* 不显示描边颜色,为什么?没有设置线宽
* 设置线宽,还是不显示,为什么?因为绘制路径不对。
* 即填充又描边CGContextDrawPath:kCGPathFillStroke。
- (void)drawRect:(CGRect)rect{
// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.拼接路径
UIBezierPath *path = [UIBezierPath bezierPath];
CGPoint startP = CGPointMake(10, 10); // 起点(A点)
[path moveToPoint:startP]; // 设置起点(A点)
[path addLineToPoint:CGPointMake(125, 125)]; //(B点)
[path addLineToPoint:CGPointMake(240, 10)]; //(C点)
// 方法一:
// 从路径的终点连接到起点
// [path addLineToPoint:startP]; // 回到起点A
// 方法二:
// 关闭路径
[path closePath];
// 3.把路径添加到上下文
CGContextAddPath(ctx, path.CGPath);
// 既填充又描边
// 设置填充颜色
[[UIColor blueColor] setFill];
// 设置描边颜色
[[UIColor redColor] setStroke];
// 设置描边,15为描边宽度
CGContextSetLineWidth(ctx, 15);
// 4.渲染上下文
// 描边
// CGContextStrokePath(ctx);
// 带有填充的渲染
// CGContextFillPath(ctx);
// 既填充又描边 kCGPathFillStroke
CGContextDrawPath(ctx, kCGPathFillStroke);
}
描边 填充 既填充又描边
* rectangle矩形
- (void)drawRect:(CGRect)rect{
// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.拼接路径
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(10, 10, 200, 200)];
// 设置圆角:圆角率:20,如果是100(半径为100),则就是个圆了
path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(10, 10, 200, 200) cornerRadius:20];
// 3.把路径添加到上下文
CGContextAddPath(ctx, path.CGPath);
// 4.渲染上下文
CGContextStrokePath(ctx);
}
* circle圆:为什么传入矩形,因为圆内切与矩形
// 画圆/椭圆
- (void)drawRect:(CGRect)rect{
// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.拼接路径
// 椭圆
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(10, 10, 200, 100)];
// 圆
// UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(10, 10, 200, 200)];
// 3.把路径添加到上下文
CGContextAddPath(ctx, path.CGPath);
// 4.渲染上下文
CGContextStrokePath(ctx);
}
椭圆 圆
* arc圆弧
PPT分析:
1>圆弧属于圆的一部分,因此先要有圆,才有弧。
2>圆需要起点吗?画线需要,圆也不另外。
3>起点在哪?圆心右边
4>画圆弧还需要起始角度,结束角度,方向,角度必须是弧度
- (void)drawRect:(CGRect)rect{
// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.拼接路径
CGPoint center = CGPointMake(125, 125); // 圆心
CGFloat radius = 100; // 半径
CGFloat startA = 0; // 起点(弧度制)
CGFloat endA = M_PI_2; // 终点(弧度制)
// clockwise:顺时针:YES,逆时针:NO
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];
// 3.把路径添加到上下文
CGContextAddPath(ctx, path.CGPath);
// 4.渲染上下文
CGContextStrokePath(ctx);
}
圆弧
* Quarter 1/4圆
* 画个1/4圆弧,Stroke
* 填充路径,Fill
* 必须有封闭路径才能填充,没有封闭路径,系统会自动关闭路径,再去填充
* 画线连接圆心,自动关闭路径。
- (void)drawRect:(CGRect)rect{
// 画1/4圆
// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.拼接路径
CGPoint center = CGPointMake(125, 125);
CGFloat radius = 100;
CGFloat startA = 0;
CGFloat endA = M_PI_2;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];
// 添加一条线到圆心(从圆弧的终点到圆心)
[path addLineToPoint:center];
// 3.把路径添加到上下文
CGContextAddPath(ctx, path.CGPath);
// 4.渲染上下文
// 默认填充封闭的图形,若不是封闭则自动把起始点和终点连起来,做成封闭图形再填充
CGContextFillPath(ctx);
}
没有添加到圆心的线(自动关闭) 添加到圆心的线
3> 重绘-下载进度条(setNeedsDisplay)
* 分析有几个控件:UISliderView,自定义view用来画图,UILabel
* 分析思路:滑动UISliderView的时候,把UISliderView的值传给自定义view,改变的显示。
* 怎么监听UISliderView,valueChange事件
* 自定义view搞一个属性接收滑动的值
* 接下来先搞label,因为他比较简单,做东西,先从简单的着手。
* 懒加载label,位置居中,文字居中
* label怎么显示?重写progress的set方法,有数据就展示在label上。
* 绘制圆弧,PPT分析,从哪开始画圆弧。
* 每次转多少°?角度 = 进度 * M_PI *2
* startAngle = -M_PI_2
* endAngle = -M_PI_2 +进度 * M_PI *2
* 不会及时更新视图显示?为什么,因为drawRect只会在视图显示的时候调用一次。
* 怎么重绘?
* 手动调用drawRect方法,不行,因为你自己不能创建上下文,必须系统调用来调用
* setNeedsDisplay:在view上做一个重绘的标记,在下一次绘图的周期来临,就会先创建好上下文,然后自动调用drawRect重绘。
// HMProgressView.h
#import <UIKit/UIKit.h>
@interface HMProgressView : UIView
@property (nonatomic, assign) CGFloat progress;
@end
// HMProgressView.m
#import "HMProgressView.h"
@interface HMProgressView()
@property (nonatomic, weak) UILabel *label;
@end
@implementation HMProgressView
- (UILabel *)label{
if (_label == nil) {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
label.textAlignment = NSTextAlignmentCenter;
[self addSubview:label];
_label = label;
}
return _label;
}
- (void)setProgress:(CGFloat)progress{
_progress = progress;
self.label.text = [NSString stringWithFormat:@"%.2f%%",progress * 100];
// 系统还没创建上下文,所以不能在这里调用
// [self drawRect:self.bounds];
// 重新绘制
// 在view上做一个重绘的标记,当下次屏幕刷新的时候,就会调用drawRect.
[self setNeedsDisplay];
}
// 当视图显示的时候会调用 默认只会调用一次
- (void)drawRect:(CGRect)rect{
// 1.获取上下文(是获取,意味着系统已经创建完毕了,这里只是获取到)
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.拼接路径
CGPoint center = CGPointMake(50, 50);
CGFloat radius = 50 - 2;
CGFloat startA = -M_PI_2;
CGFloat endA = -M_PI_2 + _progress * M_PI * 2;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];
// 3.把路径添加到上下文
CGContextAddPath(ctx, path.CGPath);
// 4.把上下文渲染到视图
CGContextStrokePath(ctx);
}
@end
// HMViewController.m
#import "HMViewController.h"
#import "HMProgressView.h"
@interface HMViewController ()
@property (weak, nonatomic) IBOutlet HMProgressView *progressView;
@end
@implementation HMViewController
- (IBAction)valueChange:(UISlider *)sender {
_progressView.progress = sender.value;
}
@end
4>绘制饼图
* startA = endA angle = 比例 *总度数360° endA = startA + angle
* angle = 自己 /100.0 * 360
* 一个一个扇形画
注意:1> 如渲染之前,添加了填充颜色,则渲染的时候会以最后的填充颜色为准把之前的所有路径都填充为此颜色;要想不同的路径填充颜色不同,则需要分别渲染!
2> 随机颜色
// 随机颜色
+ (UIColor *)randomColor{
/*
颜色有两种表现形式 RGB RGBA
RGB 24
R,G,B每个颜色通道8位
8的二进制 255
R,G,B每个颜色取值 0 ~255
120 / 255.0
*/
CGFloat r = arc4random_uniform(256) / 255.0;
CGFloat g = arc4random_uniform(256) / 255.0;
CGFloat b = arc4random_uniform(256) / 255.0;
return [UIColor colorWithRed:r green:g blue:b alpha:1];
}
5>UIKit封装绘图方法演练(不需要上下文,上下文已经封装在方法中)
5.0 绘制文本
- (void)drawRect:(CGRect)rect{
NSString *text = @"hello motolosd";
CGRect textFrame = CGRectMake(0, 0, 250, 250);
NSDictionary *dict = @{NSFontAttributeName : [UIFont systemFontOfSize:20],
NSForegroundColorAttributeName : [UIColor redColor],};
[text drawInRect:textFrame withAttributes:dict];
}
- (void)drawRect:(CGRect)rect{
NSString *text = @"hello motolosd";
CGRect textFrame = CGRectMake(0, 0, 100, 100);
NSDictionary *dict = @{NSFontAttributeName : [UIFont systemFontOfSize:20],
NSForegroundColorAttributeName : [UIColor redColor],};
// 文字所在的矩形
// 先画矩形,再画文字,这样文字就可以在矩形中显示
UIRectFill(textFrame);
[text drawInRect:textFrame withAttributes:dict];
}
- (void)drawRect:(CGRect)rect{
NSString *text = @"hello motolosd";
CGRect textFrame = CGRectMake(0, 0, 250, 250);
NSDictionary *dict = @{NSFontAttributeName : [UIFont systemFontOfSize:50],
NSForegroundColorAttributeName : [UIColor redColor],
NSStrokeWidthAttributeName : @5 // 描边(有镂空的效果)
};
[text drawInRect:textFrame withAttributes:dict];
}
- (void)drawRect:(CGRect)rect{
NSString *text = @"hello motolosd hello motolosd hello motolosd hello motolosd hello motolosd hello motolosd hello motolosd hello motolosd hello motolosd hello motolosd hello motolosd hello motolosd hello motolosd ";
CGRect textFrame = CGRectMake(0, 0, 250, 250);
NSDictionary *dict = @{NSFontAttributeName : [UIFont systemFontOfSize:20],
NSForegroundColorAttributeName : [UIColor redColor],};
// 文字会自动换行
// [text drawInRect:textFrame withAttributes:dict];
// 文字不会自动换行
[text drawAtPoint:CGPointZero withAttributes:dict];
}
5.1 绘制矩形
5.2 绘制图像
5.3 小结:绘制基本的图像,比如,直线、圆弧、圆、椭圆、矩形、三角形等,用Quartz2D-1来做,而,写进文字,如NSString类型的,还有花图片,则使用UIKit已经封装好的方法,比如drawAtPoint: withAttributes:,此时上下文已经封装好了,不需要自己获取上下文;
5.4 图像裁切 ->指定裁切区域之后,所有绘图信息都只显示裁切区域内
- (void)drawRect:(CGRect)rect
{
UIImage *image = [UIImage imageNamed:@"头像"];
// 直接画上去
// [image drawAtPoint:CGPointZero];
// 把图片画上去的同时设置图片大小
// [image drawInRect:CGRectMake(0, 0, 100, 100)];
// 设置裁剪区域,超出裁剪区域的都会被裁剪掉
UIRectClip(CGRectMake(0, 0, 100, 100));
UIImage *pImage = [UIImage imageNamed:@"001"];
// 平铺(有裁剪则裁,没裁剪则图片平铺整个view)
[pImage drawAsPatternInRect:rect];
}
直接画上去 设置图片大小平铺 裁剪
6>模仿UIImageView/雪花(定时器)
// 加载xib完毕就调用,允许定时器被触发
- (void)awakeFromNib{
// 不用这个刷新,容易卡顿
// 定时器触发已经封装好了,不需要额外触发定时器
// [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(setNeedsDisplay) userInfo:nil repeats:YES];
// iphone每秒刷新60次
// 屏幕刷新的时候就会触发
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(setNeedsDisplay)];
// 添加到运行循环上,让运行循环在屏幕刷新的时候来触发定时器
[link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
7>图形上下文栈
1.将当前的上下文copy一份,保存到栈顶(那个栈叫做”图形上下文栈”)
void CGContextSaveGState(CGContextRef c)
2.将栈顶的上下文出栈,替换掉当前的上下文
void CGContextRestoreGState(CGContextRef c)
1> 渲染的时候, 会根据上下文中的绘图状态把绘图信息给修改了,然后渲染到view上;
2> 渲染之后,绘图信息里就没东西了,绘图状态依然保存有之前设置的状态;
3> 获得上下文之后,可以把上下文通过 CGContextSaveGState的方式拷贝到栈中(包括绘图信息和状态一起拷贝)
4>要想恢复上下文的原始信息,用CGContextRestoreGState让上下文从上下文栈中出栈,并替换换来当前的栈(当前的栈的状态可能会被设置)
- (void)drawRect:(CGRect)rect{
// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 把ctx拷贝一份放在栈中
CGContextSaveGState(ctx);
// 2.拼接路径(绘图的信息)
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(10, 125)];
[path addLineToPoint:CGPointMake(240, 125)];
// 3.路径添加到上下文
CGContextAddPath(ctx, path.CGPath);
// 设置绘图的状态
[[UIColor redColor] set];
CGContextSetLineWidth(ctx, 10);
CGContextSetLineCap(ctx, kCGLineCapRound);
// 4.渲染
CGContextStrokePath(ctx);
// 第二根线
UIBezierPath *path1 = [UIBezierPath bezierPath];
[path1 moveToPoint:CGPointMake(125, 10)];
[path1 addLineToPoint:CGPointMake(125, 240)];
CGContextAddPath(ctx, path1.CGPath);
// 把栈顶上下文取出来,替换当前上下文
CGContextRestoreGState(ctx);
// 设置绘图的状态
// [[UIColor blackColor] set];
// CGContextSetLineWidth(ctx, 1);
// CGContextSetLineCap(ctx, kCGLineCapButt);
// 4.渲染
CGContextStrokePath(ctx);
}
8> 矩阵变换
- (void)drawRect:(CGRect)rect{
// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 注意:你的路径一定放在上下文矩阵操作之后
// 平移上下文
CGContextTranslateCTM(ctx, 50, 100);
// 旋转上下文
CGContextRotateCTM(ctx, M_PI_4);
// 缩放上下文
CGContextScaleCTM(ctx, 0.5, 1.2);
// 2.拼接路径
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-50, -100, 150, 200)];
// 3.把路径添加到上下文
CGContextAddPath(ctx, path.CGPath);
// 设置颜色
[[UIColor yellowColor] set];
// 4.渲染
CGContextFillPath(ctx);
}
没有经过任何矩阵处理 平移之后平移+旋转 平移+旋转+缩小(y缩小0.2)平移+旋转+缩放(y放大1.2)