画板

本文介绍了一个自定义绘画应用的设计与实现过程,包括路径模型、自定义按钮、工具栏及绘画视图等功能模块。通过组合使用iOS图形绘制API,实现了丰富的交互体验,如颜色选取、线宽调整、橡皮擦、撤销及清屏等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.把一条条路径当成model,有线宽,颜色,存进数组,画到视图上,

PathModel.h

// line --- model

//颜色\路径\宽度


@interface PathModel : NSObject


@property (nonatomic, assign) CGMutablePathRef path;

@property (nonatomic, strong) UIColor *color;

@property (nonatomic, assign) CGFloat width;


PathModel.m

- (void)dealloc

{

//释放路径

    CGPathRelease(_path);

}


- (void)setPath:(CGMutablePathRef)path

{

    if (_path != path) {

        CGPathRelease(_path);

        _path = (CGMutablePathRef)CGPathRetain(path);

    }

}


2.自定义Button(UIControl 画)

ToolButton.h


#import <UIKit/UIKit.h>

//定义button的类型

typedef enum : NSUInteger{

    kToolButtonTitleType,

    kToolButtonColorType,

    kToolButtonWidthType,

    kToolButtonNoneType

} kToolButtonType;


@interface ToolButton : UIControl

//是否选中

@property (nonatomic, assign) BOOL checked;


@property (nonatomic, copy) NSString *title;

@property (nonatomic, strong) UIColor *color;

@property (nonatomic, assign) CGFloat width;


@property (nonatomic, assign) kToolButtonType type;


@end

ToolButton.m

#import "ToolButton.h"


@implementation ToolButton


- (void)drawRect:(CGRect)rect {


    CGContextRef context = UIGraphicsGetCurrentContext();

    

    if (self.type == kToolButtonTitleType || self.type == kToolButtonWidthType) {

        //绘制标题

        NSMutableParagraphStyle *style = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];

        style.alignment = NSTextAlignmentCenter;

        

        NSDictionary *attr = @{

                               NSFontAttributeName: [UIFont boldSystemFontOfSize:20],

                               NSParagraphStyleAttributeName : style

                               };

        

        [self.title drawInRect:rect withAttributes:attr];

        

       

    } else if (self.type == kToolButtonColorType) {

        

        //画内切圆

        CGContextAddEllipseInRect(context, rect);

        

        //CGContextSetFillColorWithColor(context, self.color.CGColor);

        [self.color setFill];

        

        CGContextDrawPath(context, kCGPathFill);

    }

    

    //绘制选中效果

    if (self.checked) {

        [[UIColor blackColor] set];

        UIRectFrame(rect);

    }

   

    

}


- (void)setChecked:(BOOL)checked

{

    _checked = checked;

    

    [self setNeedsDisplay];

}


@end

3.工具栏
ToolView.h

#import <UIKit/UIKit.h>


//block类型

typedef void(^ChangeColorBlock)(UIColor *);

typedef void(^ChangeWidthBlock)(CGFloat);


typedef void(^ChangeBlock)(void);


@class ToolButton;

@interface ToolView : UIView

{

    UIView *_buttonView;    //菜单栏

    UIView *_colorView;     //颜色

    UIView *_widthView;     //线宽

    

    

    //声明block

    ChangeColorBlock _colorBlock;

    ChangeWidthBlock _widthBlock;

    

    ChangeBlock _eraseBlock;

    ChangeBlock _undoBlock;

    ChangeBlock _clearBlock;

}


@property (nonatomic, strong) ToolButton *lastButton;

@property (nonatomic, strong) ToolButton *lastColor;

@property (nonatomic, strong) ToolButton *lastWidth;


@property (nonatomic, copy) ChangeColorBlock colorBlock;

@property (nonatomic, copy) ChangeWidthBlock widthBlock;


- (void)addErase:(ChangeBlock)eraseBlock undo:(ChangeBlock)undoBlock clear:(ChangeBlock)clearBlock;


ToolView.m

- (instancetype)initWithFrame:(CGRect)frame

{

    self = [super initWithFrame:frame];

    if (self) {

        

        [self createMenuView];

        

        [self creatColorView];

        

        [self createLineWidthView];

        

    }

    return self;

}


#pragma mark - 创建子视图

//菜单栏

- (void)createMenuView

{

    //菜单栏的父视图

    _buttonView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.bounds), 25)];

    _buttonView.backgroundColor = [UIColor clearColor];

    [self addSubview:_buttonView];

    

    NSArray *titles = @[@"颜色", @"线宽", @"橡皮", @"撤销", @"清屏"];


    CGFloat width = CGRectGetWidth(self.bounds) / titles.count;

    

    //创建5个按钮

    for (int i = 0; i < titles.count; i++) {

        ToolButton *button = [[ToolButton allocinitWithFrame:CGRectMake(i * width, 0, width, 25)];

        //指定buttontitle类型

        button.type = kToolButtonTitleType;

        button.title = titles[i];

        

        button.backgroundColor = [UIColor clearColor];

        button.tag = 100 + i;

        

        [button addTarget:self

                   action:@selector(buttonAction:)

         forControlEvents:UIControlEventTouchUpInside];

        

        [_buttonView addSubview:button];

    }

}


//颜色栏

- (void)creatColorView

{

    _colorView = [[UIView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(_buttonView.frame), CGRectGetWidth(self.bounds), 40)];

    _colorView.backgroundColor = [UIColor clearColor];

    _colorView.hidden = YES;

    [self addSubview:_colorView];

    

    NSArray *colors = @[

                        [UIColor darkGrayColor],

                        [UIColor redColor],

                        [UIColor greenColor],

                        [UIColor blueColor],

                        [UIColor yellowColor],

                        [UIColor orangeColor],

                        [UIColor purpleColor],

                        [UIColor brownColor],

                        [UIColor blackColor]

                        ];

    

    CGFloat width = CGRectGetWidth(self.bounds) / colors.count;

    //创建按钮

    for (int i = 0; i < colors.count; i++) {

        ToolButton *button = [[ToolButton allocinitWithFrame:CGRectMake(i * width, 0, width, width)];

        

        button.type = kToolButtonColorType;

        button.color = colors[i];

        

        button.backgroundColor = [UIColor clearColor];

        

        [button addTarget:self

                   action:@selector(tapColor:)

         forControlEvents:UIControlEventTouchUpInside];

        

        [_colorView addSubview:button];

    }

}


//线宽栏

- (void)createLineWidthView

{

    _widthView = [[UIView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(_buttonView.frame), CGRectGetWidth(self.bounds), 40)];

    _widthView.hidden = YES;

    _widthView.backgroundColor = [UIColor clearColor];

    [self addSubview:_widthView];

    

    NSArray *widths = @[@1.0,@3.0,@5.0,@8.0,@10.0,@15.0,@20.0];

    

    CGFloat width = CGRectGetWidth(self.bounds) / widths.count;

    //创建按钮

    for (int i = 0; i < widths.count; i++) {

        ToolButton *button = [[ToolButton allocinitWithFrame:CGRectMake(i * width, 0, width, 25)];

        

        button.type = kToolButtonWidthType;

        button.title = [NSString stringWithFormat:@"%@", widths[i]];

        button.width = [widths[i] floatValue];

        

        button.backgroundColor = [UIColor clearColor];

        

        [button addTarget:self

                   action:@selector(tapWidth:)

         forControlEvents:UIControlEventTouchUpInside];

        

        [_widthView addSubview:button];

    }

}


#pragma mark - Actions

- (void)buttonAction:(ToolButton *)sender

{

    //1.把上一个button选中状态取消

    self.lastButton.checked = NO;

    

    //2.把点击的button设置为选中

    sender.checked = YES;

    

    //3.记录下选择的button

    self.lastButton = sender;

    

    //点击button,切换视图

    switch (sender.tag - 100) {

        case 0:

            _colorView.hidden = NO;

            _widthView.hidden = YES;

            break;

        case 1:

            _colorView.hidden = YES;

            _widthView.hidden = NO;

            break;

        case 2: //橡皮

            _eraseBlock();

            break;

        case 3: //撤销

            _undoBlock();

            break;

        case 4: //清屏

            _clearBlock();

            break;

        default:

            break;

    }

 

}


//颜色

- (void)tapColor:(ToolButton *)sender

{

    //1.把上一个button选中状态取消

    self.lastColor.checked = NO;

    

    //2.把点击的button设置为选中

    sender.checked = YES;

    

    //3.记录下选择的button

    self.lastColor = sender;


    //block调用

    if (_colorBlock != nil) {

        _colorBlock(sender.color);

    }

}


//线宽

- (void)tapWidth:(ToolButton *)sender

{

    //1.把上一个button选中状态取消

    self.lastWidth.checked = NO;

    

    //2.把点击的button设置为选中

    sender.checked = YES;

    

    //3.记录下选择的button

    self.lastWidth = sender;

    

    if (_widthBlock != nil) {

        _widthBlock(sender.width);

    }

}


- (void)addErase:(ChangeBlock)eraseBlock undo:(ChangeBlock)undoBlock clear:(ChangeBlock)clearBlock

{

    _eraseBlock = eraseBlock;

    _undoBlock = undoBlock;

    _clearBlock = clearBlock;

}


@end

4.绘画视图
PanelView.h

#import <UIKit/UIKit.h>


@interface PanelView : UIView

{

    NSMutableArray *_pathes;

}


@property (nonatomic, assign) CGMutablePathRef currentPath;

@property (nonatomic, strong) UIColor *currentColor;

@property (nonatomic, assign) CGFloat currentWidth;


//清屏

- (void)clear;

//撤销

- (void)undo;


@end

PanelView.m

@implementation PanelView

/*

 * 绘制多条线:

 * 1. 初始化就创建路径,一直使用同一条路径

 * 2. 每创建一个路径,就保存在数组中

 */


- (instancetype)initWithFrame:(CGRect)frame

{

    self = [super initWithFrame:frame];

    if (self) {

//       _path = CGPathCreateMutable();

        

        _pathes = [NSMutableArray array];

        

        //设置默认的画笔

        _currentColor = [UIColor blackColor];

        _currentWidth = 5;


    }

    return self;

}



- (void)drawRect:(CGRect)rect {

    

    //上下文

    CGContextRef context = UIGraphicsGetCurrentContext();

    

    /*

    //从数组中取出路径,绘制

    for (id path in _pathes) {

        

        CGMutablePathRef lastPath = (__bridge CGMutablePathRef)(path);

        

        CGContextAddPath(context, lastPath);

        

        //设置颜色和线宽

        //[[UIColor redColor] set];

        

//        CGContextSetRGBStrokeColor(context, 0, 0, 0, 1)

        

//        CGContextSetLineWidth(context, <#CGFloat width#>)

        

        CGContextDrawPath(context, kCGPathStroke);

        

    }

     */

    //遍历所有的路径,把路径绘制

    for (PathModel *model in _pathes) {

        //路径添加到上下文

        CGContextAddPath(context, model.path);

        //设置上下文(颜色)

        [model.color set];

        //(线宽)

        CGContextSetLineWidth(context, model.width);

        //绘制

        CGContextDrawPath(context, kCGPathStroke);

    }

    

    //第一次绘制的路径如果为空,就不绘制

    if (_currentPath != nil) {

        

        //路径添加到上下文

        CGContextAddPath(context, _currentPath);

        

        //设置上下文

        CGContextSetLineWidth(context, _currentWidth);

        [_currentColor set];

        

        //绘制

        CGContextDrawPath(context, kCGPathStroke);

        

        /*

        PathModel *model = [_pathes objectAtIndex:0];

        CGContextAddPath(context, model.path);

        [model.color set];

        CGContextSetLineWidth(context, model.width);

         */

    }

}


//触摸开始

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

{

    UITouch *touch = [touches anyObject];

    CGPoint location = [touch locationInView:self];

    

    //创建路径

    _currentPath = CGPathCreateMutable();

    //设置起始点

    CGPathMoveToPoint(_currentPath, NULL, location.x, location.y);

    

}


//触摸移动

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

{

    UITouch *touch = [touches anyObject];

    CGPoint location = [touch locationInView:self];

    

    //移动,路径上添加线

    CGPathAddLineToPoint(_currentPath, NULL, location.x, location.y);

    

    //手动调用drawRect:

    [self setNeedsDisplay];

    

}


//触摸结束

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

{

    /*

    UITouch *touch = [touches anyObject];

    CGPoint location = [touch locationInView:self];

    

    //路径上添加结束点

    CGPathAddLineToPoint(_currentPath, NULL, location.x, location.y);

     */

    

    //把路径保存 注意桥接

    //[_pathes addObject:(__bridge id)(_currentPath)];

    

//    if (_pathes == nil) {

//        _pathes = [NSMutableArray array];

//    }

    

    //创建model,保存绘图的属性

    PathModel *model = [[PathModel alloc] init];

    model.path = _currentPath;

    model.color = _currentColor;

    model.width = _currentWidth;

    

    [_pathes addObject:model];

    

    //释放路径

    CGPathRelease(_currentPath);_currentPath = nil;

}



#pragma mark - 图形界面方法

- (void)clear

{

    [_pathes removeAllObjects];

//    _pathes = [NSMutableArray array];

    

//    _pathes = nil;

    

    //调用drawRect方法,重绘界面

    [self setNeedsDisplay];

}


- (void)undo

{

    [_pathes removeLastObject];

    

    [self setNeedsDisplay];

}



@end


ViewController.m

#define kStatusBarHeight    20

#define kScreenWidth        ([UIScreen mainScreen].bounds.size.width)

@interface ViewController ()


@end


@implementation ViewController


- (void)viewDidLoad {

    [super viewDidLoad];

    

    PanelView *panel = [[PanelView alloc] initWithFrame:self.view.bounds];

    panel.backgroundColor = [UIColor whiteColor];

    [self.view addSubview:panel];


    

    ToolView *tool = [[ToolView alloc] initWithFrame:CGRectMake(0, kStatusBarHeight, kScreenWidth, 100)];

    tool.backgroundColor = [UIColor grayColor];

    [self.view addSubview:tool];

    

    //block赋值,设置block来修改相应的属性

    tool.colorBlock = ^(UIColor *color){

        panel.currentColor = color;

    };

    

    tool.widthBlock = ^(CGFloat width){

        panel.currentWidth = width;

    };

    

    [tool addErase:^{

        panel.currentColor = [UIColor whiteColor];

        panel.currentWidth = 20;

    } undo:^{

        [panel undo];

    } clear:^{

        [panel clear];

    }];


}

效果图






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值