UI高级 一一 画板


本文源代码请参考: https://github.com/coderZYGui/drawingBoard


利用Quartz2D 实现画板功能,效果如下图



功能实现步骤:

1.界面搭建(使用autolayout自动布局),顶部使用Toorbar来管理多个item

中间画板是一个View,底部一个View上放一个UISlider和三个Button,并做布局操作.

2. 实现画线: 每条线都是新的path,所以把每个path都保存在数组中,画线的时候从数组中取出.

3. 设置属性功能: 设置清屏,撤销,UISlider等相关业务逻辑.因为清屏,撤销,橡皮擦,线宽,线色属于画板的功能

因此写在画板类中.

4. 绘制图片到画板: 从系统相册中选择图片后,对图片做拖动,旋转,平移等形变操作. 长按图片时,将图片绘制到画板中.

注意: 这里采用的方式是: 对一个UIView进行截屏操作,在UIView中放置一个UIImageView,将相册的image保存到UIImageView的image中.然后利用UIView的代理属性将图片传给画板的image.


代码如下:

ViewController文件

//
//  ViewController.m
//  DrawingBoard
//
//  Created by 朝阳 on 2017/10/14.
//  Copyright © 2017年 sunny. All rights reserved.
//

#import "ViewController.h"
#import "ZYDrawView.h"
#import "ZYHandleImageView.h"

@interface ViewController ()<UINavigationControllerDelegate,UIImagePickerControllerDelegate,ZYHandleImageViewDelegate>

@property (weak, nonatomic) IBOutlet ZYDrawView *drawView;


@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
}

// 操作属于谁,写在谁的类中

/**
 清屏
 */
- (IBAction)clear:(UIBarButtonItem *)sender {
    [self.drawView clear];
}

/**
 撤销
 */
- (IBAction)undo:(UIBarButtonItem *)sender {
    [self.drawView undo];
}

/**
 橡皮擦
 */
- (IBAction)eraser:(UIBarButtonItem *)sender {
    [self.drawView eraser];
}

/**
 设置线宽
 */
- (IBAction)setLineWidth:(UISlider *)slider {
    [self.drawView setLineWidth:slider.value];
}

/**
 设置线颜色
 */
- (IBAction)setLineColor:(UIButton *)button {
    [self.drawView setLineColor:button.backgroundColor];
}


/**
 照片
 */
- (IBAction)photo:(UIBarButtonItem *)sender {
    
    //从系统相册中选择一张图片
    //1. 弹出系统相册
    UIImagePickerController *pickerVC = [[UIImagePickerController alloc] init];
    //2. 弹出相册的类型
    pickerVC.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
    //3. 设置pickerVC代理
    pickerVC.delegate = self;
    //3. modal出系统相册
    [self presentViewController:pickerVC animated:YES completion:nil];
}
#pragma -mark UIImagePickerControllerDelegate
/**
 当选中系统相册中的图片时会调用

 @param picker 系统相册控制器
 @param info 存放图片的字典
 */
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
{
    NSLog(@"%@",info);
    // 系统相册是一个字典,根据key值来取出图片
    UIImage *image = info[UIImagePickerControllerOriginalImage];
    
    ZYHandleImageView *handleV = [[ZYHandleImageView alloc] initWithFrame:self.drawView.frame];
    handleV.backgroundColor = [UIColor clearColor];
    handleV.image = image;
    // 设置代理属性
    handleV.delegate = self;
    
    [self.view addSubview:handleV];
    
    
    // 系统相册modal消失
    [self dismissViewControllerAnimated:YES completion:nil];
}

#pragma -mark ZYHandleImageViewDelegate
- (void)handleImageView:(ZYHandleImageView *)handleImageView newImage:(UIImage *)newImage
{
    self.drawView.image = newImage;
}

/**
 保存
 */
- (IBAction)save:(UIBarButtonItem *)sender {
    
    //1. 开启一个位图上下文
    UIGraphicsBeginImageContextWithOptions(self.drawView.bounds.size, NO, 0);
    //2. 把drawView上的内容渲染到上下文中
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    [self.drawView.layer renderInContext:ctx];
    //3. 从上下文中生成新的图片
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    
    //把图片保存到桌面
//    NSData *data = UIImagePNGRepresentation(newImage);
//    [data writeToFile:@"/Users/sunny/Desktop/photo.png" atomically:YES];
    
    //4. 把图片保存到系统相册中
    // 注意: 弹出系统相册必须实现 image:didFinishSavingWithError:contextInfo:方法
    UIImageWriteToSavedPhotosAlbum(newImage, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
}
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
    NSLog(@"saveSuccess");
}

/**
 隐藏导航
 */
- (BOOL)prefersStatusBarHidden
{
    return YES;
}


@end


ZYDrawView文件(画板)

//
//  ZYDrawView.h
//  DrawingBoard
//
//  Created by 朝阳 on 2017/10/14.
//  Copyright © 2017年 sunny. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface ZYDrawView : UIView

@property (nonatomic, strong) UIImage *image;

// 清屏
- (void)clear;
// 撤销
- (void)undo;
// 擦除
- (void)eraser;
// 设置线宽
- (void)setLineWidth:(CGFloat)width;
// 设置线的颜色
- (void)setLineColor:(UIColor *)color;

@end

#import "ZYDrawView.h"
#import "ZYBezierPath.h"

/** 定义类扩展 */
@interface ZYDrawView ()

@property (nonatomic, strong) UIBezierPath *path;
/** 用来保存所有的path */
@property (nonatomic, strong) NSMutableArray *allPaths;
/** 线宽 */
@property (nonatomic, assign) CGFloat width;
/** 线色 */
@property (nonatomic, strong) UIColor *color;

@end

@implementation ZYDrawView

- (NSMutableArray *)allPaths
{
    if (!_allPaths) {
        self.allPaths = [NSMutableArray array];
    }
    return _allPaths;
}

- (void)awakeFromNib
{
    [super awakeFromNib];
    // 添加手势
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
    [self addGestureRecognizer:pan];
    
    
    
    // 设置一个默认线宽和线色
    self.width = 1;
    self.color = [UIColor blackColor];
    
}

// 清屏
- (void)clear
{
    [self.allPaths removeAllObjects];
    // 重绘
    [self setNeedsDisplay];
}

// 撤销
- (void)undo
{
    [self.allPaths removeLastObject];
    [self setNeedsDisplay];
}

// 擦除
- (void)eraser
{
    [self setLineColor:[UIColor whiteColor]];
}

// 设置线宽
- (void)setLineWidth:(CGFloat)width
{
    self.width = width;
    
}

// 设置线的颜色
- (void)setLineColor:(UIColor *)color
{
    self.color = color;
}

/**
 拖动手势方法
 */
- (void)pan:(UIPanGestureRecognizer *)pan
{
    // 获取当前手指所在的点
    CGPoint curP = [pan locationInView:self];
    // 判断手势状态
    if (pan.state == UIGestureRecognizerStateBegan) {
        // 创建路径
        ZYBezierPath *path = [[ZYBezierPath alloc] init];
        self.path = path;
        
        // 设置线宽
        [path setLineWidth:self.width];
        path.color = self.color;
        
        // 设置起点
        [path moveToPoint:curP];
        // 把路径保存到数组中
        [self.allPaths addObject:self.path];
        
    }else if(pan.state == UIGestureRecognizerStateChanged){
        [self.path addLineToPoint:curP];
        // 重绘,调用drawRect方法
        [self setNeedsDisplay];
    }
}

- (void)setImage:(UIImage *)image
{
    _image = image;
    // 把图片添加到数组中
    [self.allPaths addObject:image];
    // 重绘
    [self setNeedsDisplay];
}

- (void)drawRect:(CGRect)rect {
    
    for (ZYBezierPath *path in self.allPaths) {
        // 判断path的真实类型
        if ([path isKindOfClass:[UIImage class]]) {
            UIImage *image = (UIImage *)path;
            // 把图片绘制到画板中(填充整个区域)
            [image drawInRect:rect];
        }else{
            [path.color set];
            [path stroke];
        }
    }
    
}

@end

ZYBezierPath文件(继承自UIBezierPath,系统UIBezierPath功能不够用)

//
//  ZYBezierPath.h
//  DrawingBoard
//
//  Created by 朝阳 on 2017/10/14.
//  Copyright © 2017年 sunny. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface ZYBezierPath : UIBezierPath

@property (nonatomic,strong) UIColor *color;

@end

#import "ZYBezierPath.h"

@implementation ZYBezierPath

@end

ZYHandleImageView文件(分析中的UIView文件,继承UIView)

//
//  ZYHandleImageView.h
//  DrawingBoard
//
//  Created by 朝阳 on 2017/10/14.
//  Copyright © 2017年 sunny. All rights reserved.
//

#import <UIKit/UIKit.h>

@class ZYHandleImageView;
@protocol ZYHandleImageViewDelegate <NSObject>

- (void)handleImageView:(ZYHandleImageView *)handleImageView newImage:(UIImage *)newImage;

@end

@interface ZYHandleImageView : UIView

@property (nonatomic, strong) UIImage *image;

/** 代理属性 */
@property (nonatomic, weak) id<ZYHandleImageViewDelegate> delegate;

@end

#import "ZYHandleImageView.h"
/** 定义类扩展 */
@interface ZYHandleImageView ()<UIGestureRecognizerDelegate>

@property (nonatomic, strong) UIImageView *imageV;

@end

@implementation ZYHandleImageView

- (UIImageView *)imageV
{
    if (!_imageV) {
        UIImageView *imageV = [[UIImageView alloc] initWithFrame:self.bounds];
        imageV.userInteractionEnabled = YES;
        [self addSubview:imageV];
        _imageV = imageV;
        // 添加手势
        [self addGestures];
    }
    return _imageV;
}

- (void)setImage:(UIImage *)image
{
    _image = image;
    self.imageV.image = image;
}

- (void)addGestures
{
    // 拖拽手势
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]
                                   initWithTarget:self action:@selector(pan:)];
    
    [self.imageV addGestureRecognizer:pan];
    
    // 捏合
    UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinch:)];
    
    pinch.delegate = self;
    [self.imageV addGestureRecognizer:pinch];
    
    
    // 添加旋转
    UIRotationGestureRecognizer *rotation = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotation:)];
    rotation.delegate = self;
    
    [self.imageV addGestureRecognizer:rotation];
    
    // 长按
    UILongPressGestureRecognizer *longP = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];
    [self.imageV addGestureRecognizer:longP];
}

// 拖动的时候调用
- (void)pan:(UIPanGestureRecognizer *)pan
{
    // 手指移动后,相对于坐标中的偏移量
    // 此时的pan.view 就相当于 UIImageView
    CGPoint transP = [pan translationInView:pan.view];
    pan.view.transform = CGAffineTransformTranslate(pan.view.transform, transP.x, transP.y);
    // 复位
    [pan setTranslation:CGPointZero inView:pan.view];
    
}

// 捏合的时候调用
- (void)pinch:(UIPinchGestureRecognizer *)pinch
{
    pinch.view.transform = CGAffineTransformScale(pinch.view.transform, pinch.scale, pinch.scale);
    //复位
    [pinch setScale:1];
}

// 旋转的时候调用
- (void)rotation:(UIRotationGestureRecognizer *)rotation
{
    rotation.view.transform = CGAffineTransformRotate(rotation.view.transform, rotation.rotation);
    //复位
    [rotation setRotation:0];
}

// 长按的时候调用
-(void)longPress:(UILongPressGestureRecognizer *)longPress
{
    // 长按时,图片闪烁一下
    if (longPress.state == UIGestureRecognizerStateBegan) {
        
        [UIView animateWithDuration:0.3 animations:^{
            self.imageV.alpha = 0;
            
        }completion:^(BOOL finished) {
            [UIView animateWithDuration:0.3 animations:^{
                self.imageV.alpha = 1;
                
            }completion:^(BOOL finished) {
                // 把相册中的图片绘制到DrawView上
                //1. 开启一个位图上下文
                UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0);
                //2. 把imageV上的内容绘制到上下文中
                CGContextRef ctx = UIGraphicsGetCurrentContext();
                [self.layer renderInContext:ctx];
                //3. 从上下文中生成新的图片
                UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
                
                //4. 关闭上下文
                UIGraphicsEndImageContext();
                
                // 若要把newImage绘制到DrawView上,需要使用代理进行传值
                if ([self.delegate respondsToSelector:@selector(handleImageView:newImage:)]) {
                    [self.delegate handleImageView:self newImage:newImage];
                }
                
                //从父控件当中移除
                [self removeFromSuperview];
                
            }];
        }];
    }
}

//能够同时支持多个手势
-(BOOL)gestureRecognizer:(nonnull UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(nonnull UIGestureRecognizer *)otherGestureRecognizer{
    
    return YES;
}

@end


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

white camel

感谢支持~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值