自己封装画板类
前言
这次简单学习了 CG (Core Graphics 框架)这个二次元框架,我自己简单使用之后觉得这个基于C语言弄出来的框架挺好用的,就是可能OC语法用久了,有些人会有些不习惯C语言的语法了.没关系,我相信你学习OC之前肯定已经熟练C语言了,用一用就习惯了.
学习建议
开始今天的正题,注释我都写在代码中了,很详细,基本实现了画板的基础功能,你完全可以直接把我的 G_DrawingBoard.h & G_DrawingBoard.m 文件代码copy出去直接使用.使用方法我会 paste 到文章的最后.
不过我还是希望通过我的口水话,能够提高你我的业务能力
//
// G_DrawingBoard.h
// UI_高级_画板
//
// Created by Gavin Guan on 16/5/26.
// Copyright © 2016年 Gavin Guan. All rights reserved.
//
#import <UIKit/UIKit.h>
#define TEST 1
//这里值为1时,开启调试代码段,为0时,反之.
@interface G_DrawingBoard : UIView
@property (retain, nonatomic) NSMutableArray *G_paths; //存每一条路径
@property (retain, nonatomic) NSMutableArray *G_lineDic;//存每一条路径的对应信息
@property (copy, nonatomic) UIColor *G_lineColor; //存当前画笔颜色
@property (assign, nonatomic) CGFloat G_lineWidth; //存当前画笔宽度(粗细)
- (void)refreshDisplay; //刷新画板视图的方法
@end
//
// G_DrawingBoard.m
// UI_高级_画板
//
// Created by Gavin Guan on 16/5/26.
// Copyright © 2016年 Gavin Guan. All rights reserved.
//
#import "G_DrawingBoard.h"
@implementation G_DrawingBoard
//-------------------------------------------
//-------------------------------------------
#pragma mark - init方法 - 代码创建方法
- (instancetype)init
{
self = [super init];
if (self) {
//仅仅单指,背景白色,属性值初始化
self.multipleTouchEnabled = NO; //关闭多点触控
self.backgroundColor = [UIColor whiteColor];
self.G_paths = [[NSMutableArray alloc] init];
self.G_lineDic = [[NSMutableArray alloc] init];
self.G_lineColor = [UIColor blackColor];
self.G_lineWidth = 3;
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
//仅仅单指,背景白色,属性值初始化
self.multipleTouchEnabled = NO; //关闭多点触控
self.backgroundColor = [UIColor whiteColor];
self.G_paths = [[NSMutableArray alloc] init];
self.G_lineDic = [[NSMutableArray alloc] init];
self.G_lineColor = [UIColor blackColor];
self.G_lineWidth = 3;
}
return self;
}
//-------------------------------------------
#pragma mark - awakeFromNib方法 - 使用nib或者storyBoard创建方法
-(void)awakeFromNib {
//仅仅单指,背景白色,属性值初始化
self.multipleTouchEnabled = NO; //关闭多点触控
self.backgroundColor = [UIColor whiteColor];
self.G_paths = [[NSMutableArray alloc] init];
self.G_lineDic = [[NSMutableArray alloc] init];
self.G_lineColor = [UIColor blackColor];
self.G_lineWidth = 3;
}
//-------------------------------------------
#pragma mark - drawRect方法
//该方法会在视图初次加载时调用,也会在向系统申请刷新视图时调用
- (void)drawRect:(CGRect)rect {
//以下代码会逐个添加路径(信息)到绘图环境
NSMutableArray *arr = self.G_paths;
#if TEST
NSLog(@"arr count = %ld", self.G_paths.count);
#endif
//获取当前绘制环境 如果你要在视图上话多个图形,并且封装在方法中,那么每个方法中都需要
//获取一次context绘图环境(绘图上下文),这里只需获取一次
CGContextRef context = UIGraphicsGetCurrentContext();
//游历 path 数组
for (int i = 0; i < self.G_paths.count; i++) {
//获取绘制属性
NSDictionary *lineDic = self.G_lineDic[i];
UIColor *lineColor = lineDic[@"lineColor"];
CGFloat lineWidth = [lineDic[@"lineWidth"] doubleValue] ;
//设置线段颜色
CGContextSetStrokeColorWithColor(context, lineColor.CGColor);
//设置线条宽度
CGContextSetLineWidth(context, lineWidth);
//设置拐点的样式
CGContextSetLineJoin(context, kCGLineJoinRound);
//获取路径
CGMutablePathRef path = (__bridge CGMutablePathRef)(arr[i]);
//添加路径到当前绘制环境
CGContextAddPath(context, path);
//绘制路径
//绘制模式
CGContextDrawPath(context, kCGPathStroke);
//绘制模式这段代码的运行机制是这样的,就像是一个绘制断点,之前的线条宽度之类的属性
//如果后面你不修改,那么就是使用原有的,如果你修改了,就使用修改后的.
//这里for循环结束一次,再次开始的时候,又会从我设置的lineDic中获取对应的信息来处
//理下一次绘制时的"画笔"的属性,以实现画板.
}
}
//-------------------------------------------
#pragma mark - touches方法
//手指接触到屏幕时,调用一次,仅一次
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//获取唯一的触摸对象 -- 开始位置
UITouch *touch = [touches anyObject];
CGPoint locationPoint = [touch locationInView:self];
#if TEST
NSLog(@"当前触摸开始位置(%.2f , %.2f)", locationPoint.x, locationPoint.y);
#endif
//创建可变路径
CGMutablePathRef path = CGPathCreateMutable();
//路径移动到初始点 - 如同"画笔"移动到某一点准备绘制
CGPathMoveToPoint(path, NULL, locationPoint.x, locationPoint.y);
//添加 path 到路径数组
[self.G_paths addObject:(__bridge id _Nonnull)(path)];
//存储 path 信息 - 当前"画笔"属性
NSDictionary *lineDic = @{
@"lineColor" : self.G_lineColor,
@"lineWidth" : [NSNumber numberWithDouble:self.G_lineWidth]
};
[self.G_lineDic addObject:lineDic];
//手动释放路径 - 通过create方式创建的CGPathRef一定要free,不然有内存泄漏
//CGPathRelease(path);
//我多次通过代码实验,猜测通过桥接方法(如下代码)
//[self.G_paths addObject:(__bridge id _Nonnull)(path)];
//添加path时,运作机制类似于retain一次,所以这里不要free这个path
//会影响到path数组中的元素path,成为野指针,释放的方法我写在了dealloc方法中
}
//仅仅在手指移动时调用(超级频繁)
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//获取唯一的触摸对象 -- 移动路径点
UITouch *touch = [touches anyObject];
CGPoint locationPoint = [touch locationInView:self];
#if TEST
NSLog(@"当前触摸移动路径(%.2f , %.2f)", locationPoint.x, locationPoint.y);
#endif
//移动时,添加 实时点 到路径
NSArray *arr = self.G_paths;
CGMutablePathRef path = (__bridge CGMutablePathRef)([arr lastObject]);
CGPathAddLineToPoint(path, NULL, locationPoint.x, locationPoint.y);
//刷新视图
[self refreshDisplay];
}
//-------------------------------------------
#pragma mark - 刷新绘制
- (void)refreshDisplay {
//刷新视图
[self setNeedsDisplay];
//手动调用 drawRect: 方法是无效的,必须通过这个方法通知系统,然后系统自行调用 drawRect:
}
//-------------------------------------------
#pragma mark - dealloc方法
-(void)dealloc {
NSMutableArray *arr = self.G_paths;
for (int i = 0; i < self.G_paths.count; i++) {
CGMutablePathRef path = (__bridge CGMutablePathRef)(arr[i]);
CGPathRelease(path); //虽然是ARC模式,但CGPathRef还是要手动释放的.
}
}
//-------------------------------------------
//-------------------------------------------
@end
下面是使用方法的代码简介 - 写在任意 viewController.m 中
#pragma mark - 按钮测试
//撤销
- (IBAction)repeal:(UIButton *)sender {
//通过移除最后一个Object,刷新视图来实现撤销方法
[self.drawingBoard.G_paths removeLastObject];
[self.drawingBoard.G_lineDic removeLastObject];
[self.drawingBoard refreshDisplay];
}
//变色 & 粗细
- (IBAction)changeColor:(UIButton *)sender {
//这里仅仅是我自己测试时,使用的代码,你可以自己定义多个按钮
//任意修改 lineColor & lineWidth 属性 - 修改当前"画笔"的属性
if (self.drawingBoard.G_lineColor == [UIColor redColor]) {
self.drawingBoard.G_lineColor = [UIColor blackColor];
self.drawingBoard.G_lineWidth = 3;
} else {
self.drawingBoard.G_lineColor = [UIColor redColor];
self.drawingBoard.G_lineWidth = 10;
}
}
- (IBAction)clearAll:(UIButton *)sender {
//通过移除全部Object,刷新视图来实现清屏
[self.drawingBoard.G_paths removeAllObjects];
[self.drawingBoard.G_lineDic removeAllObjects];
[self.drawingBoard refreshDisplay];
}
以上就是这次全部内容,如果你喜欢,就给我点赞吧.╮(╯_╰)╭
我在之后对自己的代码又进行了一次封装优化,同时得到了高人指点再次,有了再次优化的idea,有需要的可以私我,发给你们.