在iOS中实现一个简单的画板App
在這個隨筆中,我們要為iPhone實現(xiàn)一個簡單的畫板App。
首先需要指出的是,這個demo中使用QuarzCore進(jìn)行繪畫,而不是OpenGL。這兩個都可以實現(xiàn)類似的功能,區(qū)別是OpenGL更快,但是QuarzCore更簡單。
第一步,新建Xcode項目,項目名稱就叫SimplePaint。
第二步,添加QuarzCore.framework到項目中。
第三步,創(chuàng)建一個新類,類名叫Line。它代表在iPhone的屏幕上繪畫時候的線。因為不管是畫一條直線還是一條曲線,都可以看做是多條短的直線連接起來的。那么Line需要的是什么屬性呢?簡單點就是,這條線的開始點和結(jié)束點,還有這條線的顏色。所以,打開剛剛創(chuàng)建的Line類,
修改Line.h:
然后修改Line.m:
第四步,接下來創(chuàng)建另一個類,類名叫做ColorPiker。它代表顏色選擇器,我們可以點擊多個顏色中的一個作為畫筆的顏色進(jìn)行繪畫。以下是ColorPiker.h的代碼:
aColorPikerIsSelected是一個委托,當(dāng)當(dāng)前選擇器被選中后,它可以把當(dāng)前選中的顏色選擇器的顏色值傳遞出去,傳遞給實現(xiàn)了這個委托的類。在我們的這個demo中,我們會讓畫板View的實現(xiàn)此委托。
實現(xiàn)ColorPiker.m:
第五步,接下來我們就創(chuàng)建我們的畫板View,它代表可以畫圖的那部分有效區(qū)域。創(chuàng)建一個新類叫做TouchDrawView。修改TouchDrawView.h的內(nèi)容:
剛剛上文中也提過了,我們畫圖的主要思想就是把多個短線首尾連接起來,就可以成為一條軌跡。所以我們的畫板有一個array,它的item就是Line類型,它就是滑過軌跡的所有Line的集合。我們還有一個單獨的Line對象,表示在繪畫過程中,當(dāng)前正在畫的這條線。另外有一個Color類型的屬性,表示線的顏色,也就是用來保存ColorPiker傳遞出來的顏色值。
實現(xiàn)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 *)touches
114 {
115 [self setNeedsDisplay];
116 }
117
118 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
119 {
120 [self endTouches:touches];
121 [self.undoManager endUndoGrouping];
122 }
123
124 - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
125 {
126 [self endTouches:touches];
127 }
128
129 - (BOOL)canBecomeFirstResponder
130 {
131 return YES;
132 }
133
134 - (void)didMoveToWindow
135 {
136 [self becomeFirstResponder];
137 }
138
139 - (id)initWithFrame:(CGRect)frame
140 {
141 self = [super initWithFrame:frame];
142 if (self) {
143 // Initialization code
144 }
145 return self;
146 }
147
148 @end
這個文件包含了主要的邏輯,說明下主要方法的作用:
-(id)initWithCoder:當(dāng)此view被創(chuàng)建的時候這個方法自動調(diào)用,所以你不一定必須要實現(xiàn)它;當(dāng)時當(dāng)你想在初始化的時候做一些別的工作的時候你就需要實現(xiàn)它。
-(void)drawRect:每次當(dāng)屏幕需要重新顯示或者刷新的時候這個方法會被調(diào)用。
-(void)touchBegan:當(dāng)你的手指點擊到屏幕的時候這個方法會被調(diào)用。
-(void)touchMove:當(dāng)你的手指點擊屏幕后開始在屏幕移動,它會被調(diào)用。隨著手指的移動,相關(guān)的對象會秩序發(fā)送該消息。
-(void)touchEnd:當(dāng)你的手指點擊屏幕之后離開的時候,它會被調(diào)用。
還需要講解下的是,當(dāng)每次touchMove方法中,往array中添加入了新的Line,要想在畫布中顯示出來,需要刷新下頁面,調(diào)用[self setNeedsDisplay]方法即可。
基本的主要邏輯就是這樣,我們還差什么?當(dāng)然是UI!
第六步,創(chuàng)建一個 .xib文件,叫做TouchDrawViewController.xib。在右邊的工具箱中添加一個View,再往View中加入多個子View,加多少個你隨意,因為這幾個子View是用來作為顏色選擇器的,多幾個少幾個無所謂。然后設(shè)置這幾個子View的Class為 ColorPiker,接著設(shè)置他們的背景顏色,顏色值你隨意,因為會把被選中的顏色選擇器的背景顏色直接作為參數(shù)傳出來。
我們還需要一個畫布的區(qū)域,再添加一個View,拉伸它,讓它覆蓋整個空白區(qū)域。同樣更改它的Class,改為 TouchDrawView。
我們基本快要完成了。
第七步,為剛剛的UI添加一個View Controller類,新建類文件,命名為TouchDrawViewController。修改 .h 文件為:
可以看到它實現(xiàn)了ColorPikerDelegate委托,當(dāng)ColorPiker 被選中后,色值(背景色)會傳遞到當(dāng)前類作為畫筆的顏色。
接下來連接UI和ViewController。
同時打開TouchDrawViewController.xib和TouchDrawViewController.h,Ctrl+Drop UI上的每個色塊和畫布到TouchDrawViewController.h,完成后TouchDrawViewController.h是這樣的:
實現(xiàn)TouchDrawViewController.m:
在ViewDidLoad方法中,我們把每個ColorPiker的delegate為self,然后實現(xiàn)aColorPikerIsSelected方法,這樣色值就可以傳遞過來了。
最后,設(shè)置TouchrawViewController為rootController。打開delegate.m文件,修改didFinishLaunchingWithOptions方法為:
可以了,運行它吧!
相關(guān)源代碼:github
總結(jié)
以上是生活随笔為你收集整理的在iOS中实现一个简单的画板App的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python中ones的含义和用法
- 下一篇: Groovy里读写本地文件的几种方式