</br>
前言
本文实现一个简单的手绘画板的效果,主要是记录下实现原理,虽然很简单,但是这可以是实现复杂效果的基础。毕竟t他山之石可以攻玉。
原理思路
- 在touchesBegan 方法中,每次都创建一个CAShapeLayer加载在当前视图的layer上,在touchesMoved方法中改变该 CAShapeLayer 基于UIBezierPath 的路径,即可实现绘制路径的效果。
- 创建两个容器,lines用来盛放每次创建的CAShapeLayer,canceledLines 用来盛放每次删除的layer。
- 清屏操作:移除当前视图layer上的所有子图层。并清空lines。
- 撤销操作:移除当前视图layer上的lines最后的那个图层。并移除lines最后一个图层。把这个layer放入canceledLines中。
- 恢复操作:当前视图layer加载上canceledLines中最后一个layer。移除canceledLines 中的最后一个layer,并加入到lines中。
源码实现
自定义一个UIBezierPath的子类 LGPaintpath,下面是它的初始化方法
+ (instancetype)paintPathWithLineWidth:(CGFloat)width
startPoint:(CGPoint)startP
{
LGPaintPath * path = [[self alloc] init];
path.lineWidth = width;
path.lineCapStyle = kCGLineCapRound;
path.lineJoinStyle = kCGLineCapRound;
[path moveToPoint:startP];
return path;
}
LGDrawer中的实现
-
touchesBegan 事件
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
CGPoint startP = [self pointWithTouches:touches];if ([event allTouches].count == 1) { LGPaintPath *path = [LGPaintPath paintPathWithLineWidth:_width startPoint:startP]; _path = path; CAShapeLayer * slayer = [CAShapeLayer layer]; slayer.path = path.CGPath; slayer.backgroundColor = [UIColor clearColor].CGColor; slayer.fillColor = [UIColor clearColor].CGColor; slayer.lineCap = kCALineCapRound; slayer.lineJoin = kCALineJoinRound; slayer.strokeColor = self.lineColor.CGColor; slayer.lineWidth = path.lineWidth; [self.layer addSublayer:slayer]; _slayer = slayer; [[self mutableArrayValueForKey:@"canceledLines"] removeAllObjects]; [[self mutableArrayValueForKey:@"lines"] addObject:_slayer]; } } - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // 获取移动点 CGPoint moveP = [self pointWithTouches:touches]; if ([event allTouches].count == 1) { [_path addLineToPoint:moveP]; _slayer.path = _path.CGPath; } } - (CGPoint)pointWithTouches:(NSSet *)touches { UITouch *touch = [touches anyObject]; return [touch locationInView:self]; }
清屏操作
- (void)clearScreen
{
if (!self.lines.count) return ;
[self.lines makeObjectsPerformSelector:@selector(removeFromSuperlayer)];
[[self mutableArrayValueForKey:@"lines"] removeAllObjects];
[[self mutableArrayValueForKey:@"canceledLines"] removeAllObjects];
}-
撤销操作
- (void)undo { //当前屏幕已经清空,就不能撤销了 if (!self.lines.count) return; [[self mutableArrayValueForKey:@"canceledLines"] addObject:self.lines.lastObject]; [self.lines.lastObject removeFromSuperlayer]; [[self mutableArrayValueForKey:@"lines"] removeLastObject]; }
-
恢复操作
- (void)redo
{
//当没有做过撤销操作,就不能恢复了
if (!self.canceledLines.count) return;
[[self mutableArrayValueForKey:@"lines"] addObject:self.canceledLines.lastObject];
[[self mutableArrayValueForKey:@"canceledLines"] removeLastObject];
[self drawLine];
}// 画线 - (void)drawLine{ [self.layer addSublayer:self.lines.lastObject]; }
-
截取某部分的视图
顺序不可以错 UIGraphicsBeginImageContext(_drawer.bounds.size); [_drawer.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); //render的意思是渲染 //会使图片和背景图层在结合的地方更自然
保存图片到相册中
- (void)loadImageFinished:(UIImage *)image{
UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), (__bridge void *)self);
}
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo{
NSLog(@"image = %@, error = %@, contextInfo = %@", image, error, contextInfo);
}
小结
这篇记录实现的手绘板效果,很简单,但是挺有趣。