写代码之前,先说一下涂鸦,涂鸦是指漫无目的地乱写乱画。今天我们不管乱写,因为乱写只需要一个textview。下面说说如何实现乱画的功能。
说的是一个日本人想要切腹,那他得先找把刀,最次也要弄把改锥来,至于捅几下,捅进去之后切不切得动,那就要靠实践了。咱们用不着那么血腥,但是也需要先确定一下,咱们用什么来实现涂鸦。翻了翻API文档,决定用CGContext来实现我们这一目标。
首先我们要弄明白如何才能在UIView上画出一条线来,然后再思考如何能随心所欲地画出鼠标移动的轨迹,当然如果是真机进行调试,那就是手指移动的轨迹。
CGContext的API中有如下几个函数,可以初步实现我们绘制一条线段的想法:
// 设置线的颜色为白色
CGContextSetRGBStrokeColor(context,1.0,1.0,1.0,1.0);
// 保存当前的绘画状态,将这个状态推入栈(一个专门存储绘画状态的栈)内
CGContextSaveGState(context);
//接下来是设置线段
// 设置线段起点和终点
CGContextMoveToPoint(context,40.0,30.0);
CGContextAddLineToPoint(context,280.0,30.0);
// 设置线宽
CGContextSetLineWidth(context,self.width);
//设置端点的风格
CGContextSetLineCap(context,self.cap);
//描绘路径
CGContextStrokePath(context);
// 恢复之前的状态,并再次存储
CGContextRestoreGState(context);
在这里简单做一下解释,参数中的context是一个CGContextRef类型的数据,一般我们会将其设置为
UIGraphicsGetCurrentContext()的返回值;
CGContextSetRGBStrokeColor()函数用来设置线的颜色;
CGContextSaveGState()以及CGContextRestoreGState()两个函数需要成对出现
然后在二者中间添加对描绘内容的设置,比如
CGContextMoveToPoint()函数用来设置线的起点
CGContextAddLineToPoint()用来设置线的终点
CGContextSetLineWidth()用来设置线宽
CGContextSetLineCap()用来设置线段端点的样式等等等等
还有很多,有兴趣可以去API文档中扒拉扒拉。
以上就是实现画出一条线段的简单实现,那么我们要把它们写在哪里呢
很简单,只需要创建一个UIView的子类,然后定义一个实例方法,将这些代码写在这个实例方法中
最后在UIView的-drawRect:方法中去调用这个方法。下面详细说一下:
//1.创建一个UIView的子类DrawView,并在DrawView.h中声明一个方法:
- (void)drawLine:(CGContextRef)context;
然后在DrawView.m中将其实现,实现内容就写上述的那些函数。
//2.在-drawRect:方法中调用-drawLine:方法:
- (void)drawRect:(CGRect)rect {
[selfdrawInContext:UIGraphicsGetCurrentContext()];
}
在这里需要解释一下-drawRect:方法的触发
一般来说,我们会在一个视图控制器中去初始化我们创建的DrawView子类
如果初始化DrawView时设置了它的Rect,就会在视图控制的
-loadView和-viewDidLoad两个方法之后自动触发-drawRect:。
而在其他时刻,我们需要调用-setNeedsDisplay方法来触发-drawRect:。
下面来真正实现涂鸦,先看代码:
首先是定义UIView的子类ShowLineView。
在DrawLineView.h中写三个属性并声明两个方法:
#import <UIKit/UIKit.h>
@interface DrawLineView : UIView
//存放点的数组
@property (nonatomic,retain)NSMutableArray *totalArray;
//线宽
@property (nonatomic,assign,readwrite)CGFloat width;
//用来判断是否清空所绘内容
@property (nonatomic,assign)BOOL flag;
//绘制内容
- (void)drawInContext:(CGContextRef)context;
//通过pan手势将点存进数组
- (void)recordPointsWithPan:(UIPanGestureRecognizer *)pan;
@end
#import "DrawLineView.h"
@implementation DrawLineView
-(void)dealloc
{
self.totalArray =nil;
[superdealloc];
}
- (void)drawInContext:(CGContextRef)context
{
//清空之前所绘内容
CGContextClearRect(context,self.bounds);
if (_flag) {
[selfclearPath:context];
_flag = NO;
} else {
for (NSMutableArray *array in _totalArray) {
for (int i = 1; i < array.count; i++) {
//设置画笔颜色
CGContextSetRGBStrokeColor(context,1.,1., 1.,1.);
//将当前状态推进栈内
CGContextSaveGState(context);
//设置起点
//CGPointValue是NSValue的实例方法,返回一个CGPoint
CGContextMoveToPoint(context, [array[i -1]CGPointValue].x, [array[i -1]CGPointValue].y);
//设置终点
CGContextAddLineToPoint(context, [array[i]CGPointValue].x, [array[i]CGPointValue].y);
//设置画笔宽度
CGContextSetLineWidth(context,_width);
//设置拐角风格
CGContextSetLineJoin(context,kCGLineJoinRound);
//显示所绘图形
CGContextStrokePath(context);
//将栈顶状态弹出,还原状态
CGContextRestoreGState(context);
}
}
}
}
- (void)recordPointsWithPan:(UIPanGestureRecognizer *)pan
{
//首先判断pan手势的状态,如果是开始状态,就创建一个可变数组,添加到属性totalArray中,如果不是就将pan手势所在的位置添加到totalArray的最后一个元素中
if (pan.state ==UIGestureRecognizerStateBegan) {
NSMutableArray *tempArray = [[NSMutableArrayalloc]initWithCapacity:0];
[_totalArrayaddObject:tempArray];
[tempArrayrelease];
[selfsetNeedsDisplay];
} else {
//-locationInView:方法可以获得手势所在的屏幕位置
CGPoint point = [pan locationInView:pan.view];
//由于CGPoint类型的变量并不是对象,所以不能直接存到数组中,需要先转成NSValue类型,再存入数组
[[_totalArraylastObject]addObject:[NSValuevalueWithCGPoint:point]];
[selfsetNeedsDisplay];
}
}
//清空画面
- (void)clearPath:(CGContextRef)context
{
[_totalArrayremoveAllObjects];
CGContextClearRect(context,self.bounds);
[selfsetNeedsDisplay];
}
- (void)setWidth:(CGFloat)w
{
if (_width != w) {
_width = w;
}
[selfsetNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {
[selfdrawInContext:UIGraphicsGetCurrentContext()];
}
@end
接下来是定义一个UIViewController的子类DrawLineViewController。
DrawLineViewController.h中不做修改,DrawLineViewController.m内容如下:
#import "DrawLineViewController.h"
#import "DrawLineView.h"
@interface DrawLineViewController ()
{
DrawLineView *lineView;
}
@end
@implementation DrawLineViewController
- (void)viewDidLoad {
[superviewDidLoad];
//创建一个拖拽手势
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizeralloc]initWithTarget:selfaction:@selector(pan:)];
//初始化实例变量lineView
lineView = [[DrawLineViewalloc]initWithFrame:CGRectMake(0,20,320,400)];
lineView.totalArray = [NSMutableArrayarray];
lineView.userInteractionEnabled =YES;
[lineViewaddGestureRecognizer:pan];
[pan release];
[self.viewaddSubview:lineView];
[lineViewrelease];
//控制线宽的UISlider
UISlider *wSlider = [[UISlider alloc] initWithFrame:CGRectMake(60,450,260,30)];
wSlider.minimumValue =0.5;
wSlider.maximumValue =10;
[wSlider addTarget:selfaction:@selector(changeWidth:)forControlEvents:UIControlEventValueChanged];
[self.viewaddSubview:wSlider];
[wSlider release];
//清除按钮
UIButton *button = [UIButtonbuttonWithType:UIButtonTypeRoundedRect];
button.frame =CGRectMake(0,450,50,30);
button.layer.borderWidth =0.5;
[button setTitle:@"清除"forState:UIControlStateNormal];
[button addTarget:selfaction:@selector(clear)forControlEvents:UIControlEventTouchUpInside];
[self.viewaddSubview:button];
}
- (void)pan:(UIPanGestureRecognizer *)pan
{
[lineViewrecordPointsWithPan:pan];
}
- (void)changeWidth:(UISlider *)aSlider
{
lineView.width = aSlider.value;
}
- (void)clear
{
lineView.flag =YES;
[lineViewsetNeedsDisplay];
}
- (void)didReceiveMemoryWarning {
[superdidReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
整个程序就是这样了,这个小程序是用MRC写的,需要手动管理内存。仓促之下,并没有设想太多的功能,如果有兴趣,可以自行完善,比如实现改变线条颜色,撤销上一步操作等功能。今天就是这样,周末愉快。