CGContextRef 实现简易涂鸦板

写代码之前,先说一下涂鸦,涂鸦是指漫无目的地乱写乱画。今天我们不管乱写,因为乱写只需要一个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


DrawLineView.m中写如下内容:

#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写的,需要手动管理内存。仓促之下,并没有设想太多的功能,如果有兴趣,可以自行完善,比如实现改变线条颜色,撤销上一步操作等功能。今天就是这样,周末愉快。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值