十四 iOS之 画板

这次介绍一个比较复杂一些的小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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值