Quartz 2D实现简易画图工具

2018-01-31  本文已影响82人  倪大头

import "DrawView.h"//自定义的类,继承自UIView,作为“画板”界面

DrawView.h:

#import <UIKit/UIKit.h>
@class DrawView;
@protocol drawViewDelegate <NSObject>

- (void)drawViewTouchBegan;

@end
@interface DrawView : UIView
{
    CGMutablePathRef path;
    NSMutableArray *pathModelArray;//路径保存数组
}

@property (nonatomic,strong)UIColor *lineColor;//线条颜色

@property (nonatomic,assign)CGFloat lineWidth;//线条粗细

@property (nonatomic,assign)BOOL isErase;//是否是橡皮擦模式

@property id<drawViewDelegate>myDelegate;

- (void)undoAction;

- (void)clearAction;

@end

DrawView.m:

#import "DrawView.h"
#import "PathModel.h"//自定义Model类,继承自NSObject,用来存放“画”出的线条

@implementation DrawView

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        pathModelArray = [NSMutableArray array];
        self.lineColor = [UIColor blackColor];
        self.lineWidth = 5.0;
        self.backgroundColor = [UIColor clearColor];
        self.userInteractionEnabled = YES;
    }
    return self;
}

- (void)drawRect:(CGRect)rect {
    for (PathModel *model in pathModelArray) {//先把数组里的线条画上
        //获取图形上下文
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextAddPath(context, model.path);
        if (model.isErase) {//橡皮擦模式
            CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
            [[UIColor clearColor] setStroke];//设置线条颜色
            CGContextSetLineWidth(context, 15.0);//设置线条宽度
        }else {
            [model.color setStroke];
            CGContextSetLineWidth(context, model.width);
        }
        CGContextDrawPath(context, kCGPathStroke);
    }
    
    if (path) {
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextAddPath(context, path);
        
        if (self.isErase) {//橡皮擦模式
            CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
            [[UIColor clearColor] setStroke];
            CGContextSetLineWidth(context, 15.0);//设置橡皮粗细
        }else {
            [self.lineColor setStroke];//设置画笔颜色
            CGContextSetLineWidth(context, self.lineWidth);//设置画笔粗细
        }
        
        CGContextDrawPath(context, kCGPathStroke);
    }
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self.myDelegate drawViewTouchBegan];
    UITouch *touch = [touches anyObject];
    CGPoint currentPoint = [touch locationInView:self];
    //添加初始的点
    path = CGPathCreateMutable();
    CGPathMoveToPoint(path, NULL, currentPoint.x, currentPoint.y);
}

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint currentPoint = [touch locationInView:self];
    //添加新的点
    CGPathAddLineToPoint(path, NULL, currentPoint.x, currentPoint.y);
    //重新绘图
    [self setNeedsDisplay];
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    //保存每一笔线条
    PathModel *model = [[PathModel alloc]init];
    model.color = self.lineColor;
    model.width = self.lineWidth;
    model.path = path;
    model.isErase = _isErase;
    [pathModelArray addObject:model];
    
    CGPathRelease(path);
    path = nil;
}

- (void)undoAction {//撤销
    [pathModelArray removeLastObject];
    [self setNeedsDisplay];
}

- (void)clearAction {//清屏
    [pathModelArray removeAllObjects];
    [self setNeedsDisplay];
}

@end

PathModel.h:

#import <Foundation/Foundation.h>

@interface PathModel : NSObject

@property (nonatomic,strong)UIColor *color;

@property (nonatomic,assign)CGFloat width;

@property (nonatomic,assign)CGMutablePathRef path;

@property (nonatomic,assign)BOOL isErase;//是否是橡皮擦模式

@end

PathModel.m:

#import "PathModel.h"

@implementation PathModel

- (void)setPath:(CGMutablePathRef)path {
    if (_path != path) {
        _path = (CGMutablePathRef)CGPathRetain(path);
    }
}

- (void)dealloc {
    CGPathRelease(self.path);
}

@end

两个自定义类写完了,下面实现画图功能:

#import "DrawView.h"
遵守<drawViewDelegate,UIGestureRecognizerDelegate>协议,drawViewDelegate这个协议只是做一个简单的监听,触发TouchBegan时调用代理方法
- (void)createView {
    drawView = [[DrawView alloc]initWithFrame:CGRectMake(0, 64, UI_SCREEN_WIDTH, UI_SCREEN_HEIGHT-64-44)];
    drawView.myDelegate = self;
    [self.view addSubview:drawView];
    
    //toolbar
    [self.navigationController setToolbarHidden:NO animated:YES];
    [self.navigationController.toolbar setBarStyle:UIBarStyleDefault];
    self.navigationController.toolbar.frame = CGRectMake(0, UI_SCREEN_HEIGHT-44, UI_SCREEN_WIDTH, 44);
    
    UIBarButtonItem *spaceItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
    
    UIBarButtonItem *colorItem = [[UIBarButtonItem alloc]initWithTitle:@"颜色" style:UIBarButtonItemStyleDone target:self action:@selector(colorAction)];
    
    UIBarButtonItem *eraseItem = [[UIBarButtonItem alloc]initWithTitle:@"橡皮" style:UIBarButtonItemStyleDone target:self action:@selector(eraserAction)];
    
    UIBarButtonItem *undoItem = [[UIBarButtonItem alloc]initWithTitle:@"撤销" style:UIBarButtonItemStyleDone target:self action:@selector(undoAction)];
    
    UIBarButtonItem *clearItem = [[UIBarButtonItem alloc]initWithTitle:@"清屏" style:UIBarButtonItemStyleDone target:self action:@selector(clearAction)];
    
    self.toolbarItems = @[colorItem,spaceItem,eraseItem,spaceItem,undoItem,spaceItem,clearItem];
}

调色板功能

- (void)colorAction {//调色板
    if (!paletteView) {
        paletteView = [[UIImageView alloc]initWithFrame:CGRectMake(kScaleX*5, UI_SCREEN_HEIGHT-44-kScaleY*5-kScaleY*296, kScaleX*300, kScaleY*296)];
        paletteView.image = [UIImage imageNamed:@"palette"];
        [self.view addSubview:paletteView];
        UITapGestureRecognizer *paletteViewTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(chooseColor:)];
        paletteViewTap.delegate = self;
        paletteView.userInteractionEnabled = YES;
        [paletteView addGestureRecognizer:paletteViewTap];
    }else {
        [paletteView removeFromSuperview];
        paletteView = nil;
    }
}

- (void)chooseColor:(UITapGestureRecognizer *)tap {//选择颜色
    CGPoint point = [tap locationInView:paletteView];
    
    if (CGRectContainsPoint(CGRectMake(0.0f, 0.0f, paletteView.image.size.width, paletteView.image.size.height), point)) {
        NSInteger pointX = trunc(point.x);
        NSInteger pointY = trunc(point.y);
        CGImageRef cgImage = paletteView.image.CGImage;
        NSUInteger width = paletteView.image.size.width;
        NSUInteger height = paletteView.image.size.height;
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        int bytesPerPixel = 4;
        int bytesPerRow = bytesPerPixel * 1;
        NSUInteger bitsPerComponent = 8;
        unsigned char pixelData[4] = { 0, 0, 0, 0 };
        CGContextRef context = CGBitmapContextCreate(pixelData,
                                                     1,
                                                     1,
                                                     bitsPerComponent,
                                                     bytesPerRow,
                                                     colorSpace,
                                                     kCGImageAlphaPremultipliedLast |     kCGBitmapByteOrder32Big);
        CGColorSpaceRelease(colorSpace);
        CGContextSetBlendMode(context, kCGBlendModeCopy);
        
        CGContextTranslateCTM(context, -pointX, pointY-(CGFloat)height);
        CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, (CGFloat)width, (CGFloat)height), cgImage);
        CGContextRelease(context);
        
        CGFloat red   = (CGFloat)pixelData[0] / 255.0f;
        CGFloat green = (CGFloat)pixelData[1] / 255.0f;
        CGFloat blue  = (CGFloat)pixelData[2] / 255.0f;
        CGFloat alpha = (CGFloat)pixelData[3] / 255.0f;
        
        drawView.isErase = NO;
        drawView.lineColor = [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
        NSLog(@"%lf %lf %@",point.x,point.y,drawView.lineColor);
    }
}

- (void)drawViewTouchBegan {//DrawView代理方法,关闭调色板
    if (paletteView) {
        [paletteView removeFromSuperview];
        paletteView = nil;
    }
}

橡皮、撤销、清屏,都是DrawView类方法:
原理是对存放在PathModel中线条的数组进行修改

- (void)eraserAction {//橡皮
    drawView.isErase = YES;
}

- (void)undoAction {//撤销
    [drawView undoAction];
}

- (void)clearAction {//清屏
    [drawView clearAction];
}
上一篇 下一篇

猜你喜欢

热点阅读