一. 获取与视图相关联的上下文对象
#pragma mark - 绘制视图
// 注意:drawRect方法每次都是完整的绘制视图中需要绘制部分的内容
- (void)drawRect:(CGRect)rect
{
// 1. 获取上下文
CGContextRef context = UIGraphicsGetCurrentContext();
//2.绘图方法
[self drawView:context];
}
二。创建及设置路径 (path)并且将路径添加到上下文
CGPathMoveToPoint 添加路径的开始位置
CGPathAddLineToPoint 添加路径的移动轨迹(手指移动的路线)
CGPathRelease(self.drawPath); 最后一定要记得手动释放路径对象
具体代码如下:
#pragma mark - 触摸事件
#pragma mark 触摸开始,创建绘图路径
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self];
self.drawPath = CGPathCreateMutable();
// 记录路径没有被释放
self.pathReleased = NO;
// 在路径中记录触摸的初始点
CGPathMoveToPoint(self.drawPath, NULL, location.x, location.y);
}
#pragma mark 移动过程中,将触摸点不断添加到绘图路径
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
// 可以获取到用户当前触摸的点
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self];
// 将触摸点添加至路径
CGPathAddLineToPoint(self.drawPath, NULL, location.x, location.y);
//这里是通知View进行刷新,正是因为调用了setNeedsDisplay的通知 View才能在用户手指移动过程中进行绘制
[self setNeedsDisplay];
}
#pragma mark 触摸结束,释放路径
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// 一笔画完之后,将完整的路径添加到路径数组之中
// 使用数组的懒加载
if (self.drawPathArray == nil) {
self.drawPathArray = [NSMutableArray array];
}
// 要将CGPathRef添加到NSArray之中,需要借助贝塞尔曲线对象
// 贝塞尔曲线是UIKit对CGPathRef的一个封装,贝塞尔路径的对象可以直接添加到数组
// UIBezierPath *path = [UIBezierPath bezierPathWithCGPath:self.drawPath];
DrawPath *path = [DrawPath drawPathWithCGPath:self.drawPath color:self.drawColor lineWidth:self.lineWidth];
// 需要记录当前绘制路径的颜色和线宽
[self.drawPathArray addObject:path];
CGPathRelease(self.drawPath);
// 标示路径已经被释放
self.pathReleased = YES;
// 测试线宽的代码
// self.lineWidth = arc4random() % 20 + 1.0;
}
三、设置上下文状态并绘制
次方法是在deawRect 方法中调用的,View默认deawRect 方法只调用一次,将View进行渲染
而画图我们需要实时将用户手指移动的轨迹画出来,所以每一次用户的手指移动过后都需要调用一次 [self setNeedsDisplay]; 来通知View,轨迹有改变需要重新渲染,
下面👇就是具体的渲染方法
#pragma mark 绘图视图内容的方法
- (void)drawView:(CGContextRef)context
{
// 首先将绘图数组中的路径全部绘制出来
for (DrawPath *path in self.drawPathArray) {
if (path.image == nil) {
CGContextAddPath(context, path.drawPath.CGPath);
[path.drawColor set];
CGContextSetLineWidth(context, path.lineWith);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextDrawPath(context, kCGPathStroke);
} else {
// 有图像,没路径
// CGContextDrawImage(context, self.bounds, path.image.CGImage);
[path.image drawInRect:self.bounds];
}
}
//--------------------------------------------------------
// 以下代码绘制当前路径的内容,就是手指还没有离开屏幕
// 内存管理部分提到,所有create创建的都要release,而不能设置成NULL
if (!self.pathReleased) {
// 1. 添加路径
CGContextAddPath(context, self.drawPath);
// 2. 设置上下文属性
[self.drawColor set];
CGContextSetLineWidth(context, self.lineWidth);
CGContextSetLineCap(context, kCGLineCapRound);
// 3. 绘制路径
CGContextDrawPath(context, kCGPathStroke);
}
}
参数及方法说明:
绘图状态参数
CGContextSetLineWidth 设置线宽
CGContextSetBlendMode 设置混合模式
CGContextSetShouldAntialias 设置抗锯齿效果
CGContextSetLineCap 设置线条收尾点样式
CGContextSetLineJoin 设置线条连接点样式
CGContextSetLineDash 设置虚线
绘制路径
CGContextFillPath 实心路径
CGContextFillRect 实心矩形
CGContextFillRects 多个实心矩形
CGContextFillEllipseInRect 在矩形区域中绘制实心椭圆
CGContextStrokePath 空心路径
CGContextStrokeRect 空心矩形
CGContextStrokeRectWithWidth 使用宽度绘制空心矩形
CGContextStrokeEllipseInRect 在矩形区域中绘制空心椭圆
构建路径:
CGContextBeginPath 开始一个新路径
CGContextMoveToPoint 设置路径的起点
CGContextClosePath 关闭路径
CGContextAddPath 添加路径
CGContextAddLineToPoint 在指定点添加线
CGContextAddLines 添加多条线
CGContextAddRect 添加矩形
CGContextAddRects 添加多个矩形
CGContextAddEllipseInRect 在矩形区域中添加椭圆
CGContextAddArc 添加圆弧
CGContextAddArcToPoint 在指定点添加圆弧
CGContextAddCurveToPoint 在指定点添加曲线