十四iOS之 画板

xiaoxiao2021-02-28  99

这次介绍一个比较复杂一些的小demo,一个简洁的画板,主要知识点是利用贝塞尔曲线–UIBezierPath这个类去实现手动画线的操作,这个操作在很多app中电子签名这个功能中都曾用到。 先看看示例图,这个小画板有哪些工能

1、基本的画线,可以写字,你可以滑动滑块控制画线的粗细,点击最下方颜色按钮改变画线的颜色

2、点击“撤销”,可以清除你上一步的操作,不断点击,可以不断撤销。如果点击“清屏”,就会清除所有 3、点击“橡皮擦”,可以抹掉屏幕上已经画出的线,滑动滑块可以改变橡皮擦的宽度 4、点击“照片”,可以进入系统相册选择图片 5、可以对图片进行拖动、旋转、放大缩小,然后长按图片,图片会定住,这时候可以在画板上画线 6、点击“保存”,会将你绘制的内容以图片的形式保存到系统相册中


一 、UI控件的布局,如下图,我在storyboard中直接拖入的,绿色区域包含一个toolBar、5个item和1个弹簧,红色区域是1个view,蓝色区域是一个view里包含3个button和1个slider。我直接将这个item和button的action方法拖入到ViewController中

二、 我的项目中新增了两个view,“imageHandleView”和“drawView”; 另外自定义了一个UIBezierPath类,“DrawPath”

三、 首先要写的是DrawView,在这个类里把相应的属性和方法写好,等ViewController调用

DrawView.h #import <UIKit/UIKit.h> @interface DrawView : UIView /**线宽*/ @property(nonatomic,assign)NSInteger lineWidth; /**颜色*/ @property(nonatomic,strong)UIColor * pathColor; /**照片*/ @property(nonatomic,strong)UIImage * imageDraw; //清屏 -(void)clear; //撤销 -(void)undo; @end DrawView.m #import "DrawView.h" #import "DrawPath.h" @interface DrawView() /** 自定义的路径 */ @property(nonatomic,strong)DrawPath * path; /**路径数组*/ @property(nonatomic,strong)NSMutableArray * pathArr; @end @implementation DrawView -(NSMutableArray *)pathArr { if (_pathArr == nil) { _pathArr = [NSMutableArray array]; } return _pathArr; } //仅仅加载xib的时候调用 -(void)awakeFromNib { [super awakeFromNib]; [self setUp]; } -(instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { [self setUp]; } return self; } /** 初始化 */ -(void)setUp { UIPanGestureRecognizer * pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)]; [self addGestureRecognizer:pan]; _lineWidth = 1; _pathColor = [UIColor blackColor]; } //当手指拖动的时候调用 -(void)pan:(UIPanGestureRecognizer*)pan { // NSLog(@"%s",__func__); //获取手指当前触摸点 CGPoint curP = [pan locationInView:self]; if (pan.state == UIGestureRecognizerStateBegan) { //创建贝瑟尔路径 _path = [[DrawPath alloc]init]; //设置线宽 _path.lineWidth = _lineWidth; //给路径设置颜色 _path.pathSystemColor = _pathColor; //设置路径的起点 [_path moveToPoint:curP]; //保存描述好的路径 [self.pathArr addObject:_path]; } //添加线到当前点 [_path addLineToPoint:curP]; //重绘 [self setNeedsDisplay]; } //绘制图形 //只要调用drawRact方法就会把之前的内容全部清空 - (void)drawRect:(CGRect)rect { // NSLog(@"%s",__func__); for (DrawPath * path in self.pathArr) { if ([path isKindOfClass:[UIImage class]]) { //绘制图片 UIImage * image = (UIImage *)path; [image drawInRect:rect]; } else{ //画线 [path.pathSystemColor set]; [path stroke]; } } } #pragma mark - 功能 //清屏 -(void)clear; { [self.pathArr removeAllObjects]; [self setNeedsDisplay]; } //撤销 -(void)undo { [self.pathArr removeLastObject]; [self setNeedsDisplay]; } //图片 -(void)setImageDraw:(UIImage *)imageDraw { //重新赋值 _imageDraw = imageDraw; //添加到路径中 [self.pathArr addObject:_imageDraw]; //重绘 [self setNeedsDisplay]; } @end

四、自定义UIBezierPath,为了给它添加颜色的属性

DrawPath.h #import <UIKit/UIKit.h> @interface DrawPath : UIBezierPath //因为 UIBezierPath 这个类没有颜色的属性,需要自己添加此属性 @property(nonatomic,strong)UIColor * pathSystemColor; @end

五、然后写ImageHandleView,这个view是对image进行操作用的,里面主要写入拖动、旋转、缩放、长按这个几个手势,在用户操作完成后它就会被移除

ImageHandleView.h #import <UIKit/UIKit.h> @interface ImageHandleView : UIView /**ViewController传过来的图片传给这个图片*/ @property(nonatomic,strong)UIImage * imageH; /**图片处理完成后的block*/ @property(nonatomic,strong)void(^handleCompletionBlock)(UIImage * image); @end ImageHandleView.m #import "ImageHandleView.h" @interface ImageHandleView()<UIGestureRecognizerDelegate> @property(nonatomic,weak)UIImageView * imageV; @end @implementation ImageHandleView //重写set方法,展示UIImageView的图片 -(void)setImageH:(UIImage *)imageH { _imageH = imageH; self.imageV.image = imageH; } -(UIImageView *)imageV { if (_imageV == nil) { UIImageView * imageV = [[UIImageView alloc]initWithFrame:self.bounds]; imageV.userInteractionEnabled = YES; _imageV = imageV; //添加手势 [self setUpGestureRecognizer]; [self addSubview:imageV]; } return _imageV; } #warning 开启这个方法就可以阻止DrawView的pan方法,这样在拖动图片时就不会画出线 -(void)panHandle { NSLog(@"%s",__func__); } #pragma mark - 添加手势 -(void)setUpGestureRecognizer { //添加拖动手势给ImageHandleView UIPanGestureRecognizer * panHandle = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panHandle)]; [self addGestureRecognizer:panHandle]; //平移 UIPanGestureRecognizer * pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)]; [_imageV addGestureRecognizer:pan]; //旋转 UIRotationGestureRecognizer * rotation = [[UIRotationGestureRecognizer alloc]initWithTarget:self action:@selector(rotation:)]; [_imageV addGestureRecognizer:rotation]; //缩放 UIPinchGestureRecognizer * pinch = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(pinch:)]; [_imageV addGestureRecognizer:pinch]; //长按 UILongPressGestureRecognizer * longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPress:)]; [_imageV addGestureRecognizer:longPress]; } -(void)pan:(UIPanGestureRecognizer*)pan { //获取手指的偏移量 CGPoint transP = [pan translationInView:self.imageV]; //设置UIImageView的形变 self.imageV.transform = CGAffineTransformTranslate(self.imageV.transform, transP.x, transP.y); //复位:只要想要相对于上一次就必须复位 [pan setTranslation:CGPointZero inView:self.imageV]; } -(void)rotation:(UIRotationGestureRecognizer*)rotation { self.imageV.transform = CGAffineTransformRotate(self.imageV.transform, rotation.rotation); //必须遵循代理才能支持多个手势 rotation.delegate = self; rotation.rotation = 0; } -(void)pinch:(UIPinchGestureRecognizer*)pinch { self.imageV.transform = CGAffineTransformScale(self.imageV.transform, pinch.scale, pinch.scale); pinch.delegate = self; pinch.scale = 1; } -(void)longPress:(UILongPressGestureRecognizer*)longPress { if (longPress.state == UIGestureRecognizerStateBegan) { //高亮效果 [UIView animateWithDuration:0.25 animations:^{ self.imageV.alpha = 0; }completion:^(BOOL finished) { [UIView animateWithDuration:0.25 animations:^{ self.imageV.alpha = 1; } completion:^(BOOL finished) { //高亮完成之后 //把处理的图片生成一张新的图片 //开启位图上下文 UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0); //获取上下文 CGContextRef ctx = UIGraphicsGetCurrentContext(); //把控件的图层渲染到上下文 [self.layer renderInContext:ctx]; //获取图片 UIImage * image = UIGraphicsGetImageFromCurrentImageContext(); //关闭上下文 UIGraphicsEndImageContext(); //调用block if (_handleCompletionBlock) { _handleCompletionBlock(image); } //把自己从父控件移除 [self removeFromSuperview]; }]; }]; } } #pragma mark - UIGestureRecognizerDelegate // 是否同时支持多个手势 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { return YES; } @end

六、 最后用ViewController控制以上两个view

ViewCnontroller.m #import "ViewController.h" #import "DrawView.h" #import "ImageHandleView.h" @interface ViewController ()<UINavigationControllerDelegate,UIImagePickerControllerDelegate> @property (weak, nonatomic) IBOutlet DrawView *drawView; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } #pragma mark - 清屏 - (IBAction)clear:(id)sender { [_drawView clear]; } #pragma mark - 撤销 - (IBAction)undo:(id)sender { [_drawView undo]; } #pragma mark - 橡皮擦 - (IBAction)eraser:(id)sender { _drawView.pathColor = _drawView.backgroundColor; } #pragma mark - 选择照片 - (IBAction)pickPhoto:(id)sender { //弹出系统相册 //选择控制器 UIImagePickerController * pickerVc = [[UIImagePickerController alloc]init]; //设置选择控制器来源 pickerVc.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum; pickerVc.delegate = self; [self presentViewController:pickerVc animated:YES completion:nil]; } #pragma mark - UIImagePickerControllerDelegate //当用户选择一张图片的时候调用 -(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info { //获取原始大小的图片 UIImage * image = info[UIImagePickerControllerOriginalImage]; //初始化imageHandleV ImageHandleView * imageHandleV = [[ImageHandleView alloc]initWithFrame:self.drawView.bounds]; //当用户长按图片后,会执行这个block,把图片赋值给drawView imageHandleV.handleCompletionBlock = ^(UIImage *image) { _drawView.imageDraw = image; }; [self.drawView addSubview:imageHandleV]; //把图片传过去 imageHandleV.imageH = image; //把选中的照片画到画板上(这句是没有加图片操作之前的代码,可以删掉) // _drawView.imageDraw = image; //退出相册 [self dismissViewControllerAnimated:YES completion:nil]; } #pragma mark - 保存 - (IBAction)save:(id)sender { //开启上下文 UIGraphicsBeginImageContextWithOptions(_drawView.bounds.size, NO, 0); //获取上下文 CGContextRef ctx = UIGraphicsGetCurrentContext(); //渲染图层 [_drawView.layer renderInContext:ctx]; //获取上下文中的图片 UIImage * image = UIGraphicsGetImageFromCurrentImageContext(); //关闭上下文 UIGraphicsEndImageContext(); // 保存画板的内容放入相册 // image:写入的图片 // completionTarget图片保存监听者 // 注意:以后写入相册方法中,想要监听图片有没有保存完成,保存完成的方法不能随意乱写 UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil); } //监听保存完成,必须实现这个方法 - (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo { NSLog(@"保存图片成功"); UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"保存成功!" message:nil preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction * action = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleCancel handler:nil]; [alert addAction:action]; [self presentViewController:alert animated:YES completion:nil]; } #pragma mark - 线宽控制 - (IBAction)valueChange:(UISlider *)sender { _drawView.lineWidth = sender.value; } #pragma mark - 颜色控制 - (IBAction)colorChange:(UIButton *)sender { _drawView.pathColor = sender.backgroundColor; } @end

github demo : DrawingBoard

转载请注明原文地址: https://www.6miu.com/read-28239.html

最新回复(0)