本人录制技术视频地址:https://edu.csdn.net/lecturer/1899 欢迎观看。
这一节,我将介绍使用Quartz 2D实现的一个实例 -- 涂鸦。在介绍直接,先介绍一下贝塞尔曲线的基本概念。
Bezier Path 基础
UIBezierPath对象是CGPathRef数据类型的封装。path如果是基于矢量形状的,都用直线和曲线段去创建。我们使用直线段去创建矩形和多边形,使用曲线段去创建弧(arc),圆或者其他复杂的曲线形状。每一段都包括一个或者多个点,绘图命令定义如何去诠释这些点。每一个直线段或者曲线段的结束的地方是下一个的开始的地方。每一个连接的直线或者曲线段的集合成为subpath。一个UIBezierPath对象定义一个完整的路径包括一个或者多个subpaths。
创建和使用一个path对象的过程是分开的。创建path是第一步,包含一下步骤:
(1)创建一个Bezier path对象。
(2)使用方法moveToPoint:去设置初始线段的起点。
(3)添加line或者curve去定义一个或者多个subpaths。
(4)改变UIBezierPath对象跟绘图相关的属性。
例如,我们可以设置stroked path的属性lineWidth和lineJoinStyle。也可以设置filled path的属性usesEvenOddFillRule。
我这里绘制的涂鸦效果,就是使用的UIBezierPath。效果图如下(字写的有点难看,莫怪~~~):
需求说明:
本demo支持,曲线随意画。
1. 通过点击“红绿蓝”,可以拾取自己想要的绘制颜色。
2. 通过拖拽UISlider,可以改变曲线的粗细。
3. 支持撤销、清屏、保存到相册的功能。
设计分析:
1. 每一条曲线,都有线宽,颜色,及路径信息,所以我们可以定义一个模型,用于存储这些信息。
2. 涂鸦效果是由很多条曲线构成的,所以我们可以定义一个可变数组,用于存放上面的很多模型数据。
3. 在执行撤销,清屏操作的时候,我们只需要对数组进行删除最后一条记录或者清空操作,然后调用[self setNeedsDisplay] 方法,实时绘制。
大致代码:
自定义的模型 DrawModel.h:
@interface DrawModel : NSObject
@property (nonatomic, assign) CGFloat lineNewWidth;
@property (nonatomic, strong) UIColor *lineNewColor;
@property (nonatomic, strong) UIBezierPath *path;
@end
@interface DrawView : UIView
@property (nonatomic, assign) CGFloat lineNewWidth;
@property (nonatomic, strong) UIColor *lineNewColor;
- (void)back;
- (void)clear;
@end
可变数组的定义及懒加载
@property (nonatomic,strong) NSMutableArray *infos;
- (NSMutableArray *)infos {
if (!_infos) {
_infos = [NSMutableArray array];
}
return _infos;
}
点击颜色按钮和滑动UISlider应该改变对应的值
- (void)setLineNewWidth:(CGFloat)lineNewWidth {
_lineNewWidth = lineNewWidth;
}
- (void)setLineNewColor:(UIColor *)lineColor {
if (lineColor == nil) {
_lineNewColor = [UIColor blackColor];
} else {
_lineNewColor = lineColor;
}
}
撤销及清屏操作
- (void)back {
[self.infos removeLastObject];
[self setNeedsDisplay];
}
- (void)clear {
[self.infos removeAllObjects];
[self setNeedsDisplay];
}
保存到相册大致代码
UIImage *newImage = [UIImage imageWithView:self.paintView];
UIImageWriteToSavedPhotosAlbum(newImage, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
贝塞尔曲线保存路径及模型创建代码如下
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint startPoint = [touch locationInView:touch.view];
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:startPoint];
DrawModel *model = [[DrawModel alloc] init];
model.lineNewWidth = self.lineNewWidth == 0 ? 1: self.lineNewWidth;
model.lineNewColor = self.lineNewColor;
model.path = path;
[self.infos addObject:model];
[self setNeedsDisplay];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint movePoint = [touch locationInView:touch.view];
DrawModel *model = [self.infos lastObject];
UIBezierPath *path = model.path;
[path addLineToPoint:movePoint];
[self setNeedsDisplay];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchesMoved:touches withEvent:event];
}
对代码进行大致说明:
1.每次手指按下时,会触发touchesBegan方法,每次创建一个UIBezierPath路径及创建数据模型DrawModel,并且将模型数据放到数组infos中。
2.手指拖动的时候,会触发touchesMoved方法,立刻从数组infos中取出拖拽前创建的UIBezierPath,并且将绘制所有的点的集合保存在贝塞尔曲线中。
3.手指抬起的时候,会触发touchesEnded方法,理论上只有一个点,但也应该完成最终的绘制,由于他所做得工作和touchesMoved一致,所以直接调用touchesMoved方法即可。
关键绘制代码如下:
- (void)drawRect:(CGRect)rect {
for (DrawModel *model in self.infos) {
UIBezierPath *path = model.path;
path.lineWidth = model.lineNewWidth;
path.lineCapStyle = kCGLineCapRound;
path.lineJoinStyle = kCGLineJoinRound;
[model.lineNewColor set];
[path stroke];
}
}
每次调用[self setNeedsDisplay] 方法就会触发drawRect方法。在这个方法中,我们只需要遍历infos数组,获取到每个模型数据信息,然后设置绘制相关信息就可以了。