在iOS中实现一个简单的画板App

来源:互联网 发布:中科大软件学院学费 编辑:程序博客网 时间:2024/05/01 10:26

在iOS中实现一个简单的画板App

在这个随笔中,我们要为iPhone实现一个简单的画板App。

首先需要指出的是,这个demo中使用QuarzCore进行绘画,而不是OpenGL。这两个都可以实现类似的功能,区别是OpenGL更快,但是QuarzCore更简单。
 
第一步,新建Xcode项目,项目名称就叫SimplePaint。
第二步,添加QuarzCore.framework到项目中。
 
 
第三步,创建一个新类,类名叫Line。它代表在iPhone的屏幕上绘画时候的线。因为不管是画一条直线还是一条曲线,都可以看做是多条短的直线连接起来的。那么Line需要的是什么属性呢?简单点就是,这条线的开始点和结束点,还有这条线的颜色。所以,打开刚刚创建的Line类,
修改Line.h:
 
然后修改Line.m:
 
第四步,接下来创建另一个类,类名叫做ColorPiker。它代表颜色选择器,我们可以点击多个颜色中的一个作为画笔的颜色进行绘画。以下是ColorPiker.h的代码:
 
aColorPikerIsSelected是一个委托,当当前选择器被选中后,它可以把当前选中的颜色选择器的颜色值传递出去,传递给实现了这个委托的类。在我们的这个demo中,我们会让画板View的实现此委托。
 
实现ColorPiker.m:
 
 
第五步,接下来我们就创建我们的画板View,它代表可以画图的那部分有效区域。创建一个新类叫做TouchDrawView。修改TouchDrawView.h的内容:
 
刚刚上文中也提过了,我们画图的主要思想就是把多个短线首尾连接起来,就可以成为一条轨迹。所以我们的画板有一个array,它的item就是Line类型,它就是滑过轨迹的所有Line的集合。我们还有一个单独的Line对象,表示在绘画过程中,当前正在画的这条线。另外有一个Color类型的属性,表示线的颜色,也就是用来保存ColorPiker传递出来的颜色值。
 
实现TouchDrawView.m
复制代码
  1 //  2 //  TouchDrawView.m  3 //  CaplessCoderPaint  4 //  5 //  Created by backslash112 on 14/10/29.  6 //  Copyright (c) 2014年 backslash112. All rights reserved.  7 //  8   9 #import "TouchDrawView.h" 10 #import "Common.h" 11  12 @implementation TouchDrawView 13 { 14 } 15 @synthesize currentLine; 16 @synthesize linesCompleted; 17 @synthesize drawColor; 18  19 - (id)initWithCoder:(NSCoder *)c 20 { 21     self = [super initWithCoder:c]; 22     if (self) { 23         linesCompleted = [[NSMutableArray alloc] init]; 24         [self setMultipleTouchEnabled:YES]; 25          26         drawColor = [UIColor blackColor]; 27         [self becomeFirstResponder]; 28     } 29     return self; 30 } 31  32 //  It is a method of UIView called every time the screen needs a redisplay or refresh. 33 - (void)drawRect:(CGRect)rect 34 { 35     CGContextRef context = UIGraphicsGetCurrentContext(); 36     CGContextSetLineWidth(context, 5.0); 37     CGContextSetLineCap(context, kCGLineCapRound); 38     [drawColor set]; 39     for (Line *line in linesCompleted) { 40         [[line color] set]; 41         CGContextMoveToPoint(context, [line begin].x, [line begin].y); 42         CGContextAddLineToPoint(context, [line end].x, [line end].y); 43         CGContextStrokePath(context); 44     } 45 } 46  47 - (void)undo 48 { 49     if ([self.undoManager canUndo]) { 50         [self.undoManager undo]; 51         [self setNeedsDisplay]; 52     } 53 } 54  55 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 56 { 57     [self.undoManager beginUndoGrouping]; 58     for (UITouch *t in touches) { 59         // Create a line for the value 60         CGPoint loc = [t locationInView:self]; 61         Line *newLine = [[Line alloc] init]; 62         [newLine setBegin:loc]; 63         [newLine setEnd:loc]; 64         [newLine setColor:drawColor]; 65         currentLine = newLine; 66     } 67 } 68  69 - (void)addLine:(Line*)line 70 { 71     [[self.undoManager prepareWithInvocationTarget:self] removeLine:line]; 72     [linesCompleted addObject:line]; 73 } 74  75 - (void)removeLine:(Line*)line 76 { 77     if ([linesCompleted containsObject:line]) 78         [linesCompleted removeObject:line]; 79 } 80  81 - (void)removeLineByEndPoint:(CGPoint)point 82 { 83     NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { 84         Line *evaluatedLine = (Line*)evaluatedObject; 85         return evaluatedLine.end.x == point.x && 86         evaluatedLine.end.y == point.y; 87     }]; 88     NSArray *result = [linesCompleted filteredArrayUsingPredicate:predicate]; 89     if (result && result.count > 0) { 90         [linesCompleted removeObject:result[0]]; 91     } 92 } 93  94 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 95 { 96     for (UITouch *t in touches) { 97         [currentLine setColor:drawColor]; 98         CGPoint loc = [t locationInView:self]; 99         [currentLine setEnd:loc];100         101         if (currentLine) {102             [self addLine:currentLine];103         }104         Line *newLine = [[Line alloc] init];105         [newLine setBegin:loc];106         [newLine setEnd:loc];107         [newLine setColor:drawColor];108         currentLine = newLine;109     }110     [self setNeedsDisplay];111 }112 113 - (void)endTouches:(NSSet *)touches114 {115     [self setNeedsDisplay];116 }117 118 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event119 {120     [self endTouches:touches];121     [self.undoManager endUndoGrouping];122 }123 124 - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event125 {126     [self endTouches:touches];127 }128 129 - (BOOL)canBecomeFirstResponder130 {131     return YES;132 }133 134 - (void)didMoveToWindow135 {136     [self becomeFirstResponder];137 }138 139 - (id)initWithFrame:(CGRect)frame140 {141     self = [super initWithFrame:frame];142     if (self) {143         // Initialization code144     }145     return self;146 }147 148 @end
复制代码

这个文件包含了主要的逻辑,说明下主要方法的作用:

-(id)initWithCoder:当此view被创建的时候这个方法自动调用,所以你不一定必须要实现它;当时当你想在初始化的时候做一些别的工作的时候你就需要实现它。

-(void)drawRect:每次当屏幕需要重新显示或者刷新的时候这个方法会被调用。

-(void)touchBegan:当你的手指点击到屏幕的时候这个方法会被调用。

-(void)touchMove:当你的手指点击屏幕后开始在屏幕移动,它会被调用。随着手指的移动,相关的对象会秩序发送该消息。

-(void)touchEnd:当你的手指点击屏幕之后离开的时候,它会被调用。

还需要讲解下的是,当每次touchMove方法中,往array中添加入了新的Line,要想在画布中显示出来,需要刷新下页面,调用[self setNeedsDisplay]方法即可。
 
基本的主要逻辑就是这样,我们还差什么?当然是UI!
 
第六步,创建一个 .xib文件,叫做TouchDrawViewController.xib。在右边的工具箱中添加一个View,再往View中加入多个子View,加多少个你随意,因为这几个子View是用来作为颜色选择器的,多几个少几个无所谓。然后设置这几个子View的Class为 ColorPiker,接着设置他们的背景颜色,颜色值你随意,因为会把被选中的颜色选择器的背景颜色直接作为参数传出来。
 
 
我们还需要一个画布的区域,再添加一个View,拉伸它,让它覆盖整个空白区域。同样更改它的Class,改为 TouchDrawView。
 
 
我们基本快要完成了。
 
第七步,为刚刚的UI添加一个View Controller类,新建类文件,命名为TouchDrawViewController。修改 .h 文件为:
 
可以看到它实现了ColorPikerDelegate委托,当ColorPiker 被选中后,色值(背景色)会传递到当前类作为画笔的颜色。
 
接下来连接UI和ViewController。
同时打开TouchDrawViewController.xib和TouchDrawViewController.h,Ctrl+Drop UI上的每个色块和画布到TouchDrawViewController.h,完成后TouchDrawViewController.h是这样的:
 
实现TouchDrawViewController.m:
 
在ViewDidLoad方法中,我们把每个ColorPiker的delegate为self,然后实现aColorPikerIsSelected方法,这样色值就可以传递过来了。
 
最后,设置TouchrawViewController为rootController。打开delegate.m文件,修改didFinishLaunchingWithOptions方法为:
 
可以了,运行它吧!

 

相关源代码:github

作者:backslash112 
出处:http://sirkevin.cnblogs.com/ 
0 0