在这个随笔中,我们要为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
 //
// TouchDrawView.m
// CaplessCoderPaint
//
// Created by backslash112 on 14/10/29.
// Copyright (c) 2014年 backslash112. All rights reserved.
// #import "TouchDrawView.h"
#import "Common.h" @implementation TouchDrawView
{
}
@synthesize currentLine;
@synthesize linesCompleted;
@synthesize drawColor; - (id)initWithCoder:(NSCoder *)c
{
self = [super initWithCoder:c];
if (self) {
linesCompleted = [[NSMutableArray alloc] init];
[self setMultipleTouchEnabled:YES]; drawColor = [UIColor blackColor];
[self becomeFirstResponder];
}
return self;
} // It is a method of UIView called every time the screen needs a redisplay or refresh.
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 5.0);
CGContextSetLineCap(context, kCGLineCapRound);
[drawColor set];
for (Line *line in linesCompleted) {
[[line color] set];
CGContextMoveToPoint(context, [line begin].x, [line begin].y);
CGContextAddLineToPoint(context, [line end].x, [line end].y);
CGContextStrokePath(context);
}
} - (void)undo
{
if ([self.undoManager canUndo]) {
[self.undoManager undo];
[self setNeedsDisplay];
}
} - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.undoManager beginUndoGrouping];
for (UITouch *t in touches) {
// Create a line for the value
CGPoint loc = [t locationInView:self];
Line *newLine = [[Line alloc] init];
[newLine setBegin:loc];
[newLine setEnd:loc];
[newLine setColor:drawColor];
currentLine = newLine;
}
} - (void)addLine:(Line*)line
{
[[self.undoManager prepareWithInvocationTarget:self] removeLine:line];
[linesCompleted addObject:line];
} - (void)removeLine:(Line*)line
{
if ([linesCompleted containsObject:line])
[linesCompleted removeObject:line];
} - (void)removeLineByEndPoint:(CGPoint)point
{
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
Line *evaluatedLine = (Line*)evaluatedObject;
return evaluatedLine.end.x == point.x &&
evaluatedLine.end.y == point.y;
}];
NSArray *result = [linesCompleted filteredArrayUsingPredicate:predicate];
if (result && result.count > ) {
[linesCompleted removeObject:result[]];
}
} - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *t in touches) {
[currentLine setColor:drawColor];
CGPoint loc = [t locationInView:self];
[currentLine setEnd:loc]; if (currentLine) {
[self addLine:currentLine];
}
Line *newLine = [[Line alloc] init];
[newLine setBegin:loc];
[newLine setEnd:loc];
[newLine setColor:drawColor];
currentLine = newLine;
}
[self setNeedsDisplay];
} - (void)endTouches:(NSSet *)touches
{
[self setNeedsDisplay];
} - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self endTouches:touches];
[self.undoManager endUndoGrouping];
} - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self endTouches:touches];
} - (BOOL)canBecomeFirstResponder
{
return YES;
} - (void)didMoveToWindow
{
[self becomeFirstResponder];
} - (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
} @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

在iOS中实现一个简单的画板App的更多相关文章

  1. 请问IOS中做一个手机网站的app壳复杂吗?

    公司开发了一个平台,手机网站已经做出来了,想开发一个苹果应用app,但公司没人会IOS开发,为了减小成本,现在想直接做一个壳来加载手机网站,请问在ios中复杂吗?是否有相应的控件直接加载url就行? ...

  2. (一)在HTML页面中实现一个简单的Tab

    在HTML页面中实现一个简单的Tab 为了充分利用有限的HTML页面空间,经常会采用类似与TabControl的效果通过切换来显示更多的内容.本文将采用一种最为简单的方法来实现类似如Tab页切换的效果 ...

  3. 用 Vue 做一个简单的购物app

    前言 最近在学习Vue的使用.看了官方文档之后,感觉挺有意思的.于是着手做了一个简单的购物app.h5 与原生 app 交互的原理这是我第一次在这个网站上写分享,如有不当之处,请多多指教. 一整个项目 ...

  4. express 写一个简单的web app

    之前写过一个简单的web app, 能够完成注册登录,展示列表,CURD 但是版本好像旧了,今天想写一个简单的API 供移动端调用 1.下载最新的node https://nodejs.org/zh- ...

  5. 转载 -- iOS中SDK的简单封装与使用

    一.功能总述 在博客开始的第一部分,我们先来看一下我们最终要实现的效果.下图中所表述的就是我们今天博客中要做的事情,下方的App One和App Two都植入了我们将要封装的LoginSDK, 两个A ...

  6. iOS 中CoreData的简单使用

    原文链接:http://www.jianshu.com/p/4411f507dd9f 介绍:本文介绍的CoreData不在AppDelegate中创建,在程序中新建工程使用,即创建本地数据库,缓存数据 ...

  7. 在eclipse中配置一个简单的spring入门项目

    spring是一个很优秀的基于Java的轻量级开源框架,为了解决企业级应用的复杂性而创建的,spring不仅可用于服务器端开发,从简单性.可测试性和松耦合性的角度,任何java应用程序都可以利用这个思 ...

  8. iOS 中多线程的简单使用

    iOS中常用的多线程操作有( NSThread, NSOperation GCD ) 为了能更直观的展现多线程操作在SB中做如下的界面布局: 当点击下载的时候从网络上下载图片: - (void)loa ...

  9. 如何在Liferay 7中创建一个简单的JSF Portlet

    这个将在Liferay IDE 3.1 M3的发布版中提供创建的选项,但是你也可以通过命令行来创建. 1.这是Liferay JSF团队的官网:http://liferayfaces.org/ 你能在 ...

随机推荐

  1. SSH实战 · 唯唯乐购项目(下)

    后台模块 一:后台用户模块 引入后台管理页面 创建adminuser表: CREATE TABLE `adminuser` (   `uid` int(11) NOT NULL AUTO_INCREM ...

  2. android通过webview调起支付宝app支付

    webview在加载网页的时候会默认调起手机自带的浏览器加载网页,用户体验不好.但当用户设置浏览器客户端(setWebViewClient)设置这样的监听事件之后,当请求url的时候就不会打开手机自带 ...

  3. PHP获取客户端IP

    /** * 获取客户端IP */ function getClientIp() { $ip = 'unknown'; $unknown = 'unknown'; if (isset($_SERVER[ ...

  4. AEAI DP V3.7.0 发布,开源综合应用开发平台

    1  升级说明 AEAI DP 3.7版本是AEAI DP一个里程碑版本,基于JDK1.7开发,在本版本中新增支持Rest服务开发机制(默认支持WebService服务开发机制),且支持WS服务.RS ...

  5. 免费公开课,讲解强大的文档集成组件Aspose,现在可报名

    课程①:Aspose.Total公开课内容:讲解全能型文档管理工具Aspose.Total主要功能及应用领域时间:2016-11-24 14:30 (暂定)报名地址:http://training.e ...

  6. ios 类似微信红点显示功能

    设计思路:给UIView增加一个分类 所有的视图都可以根据需要来进行红点显示 #import <UIKit/UIKit.h> @interface UIView (CHRRedDot) @ ...

  7. 【一起学OpenFoam】02 软件准备

    "工欲善其事必先利其器",在利用OpenFoam解决我们的工程问题之前,首先要做的事情是搭建一个OpenFoam运行环境.很遗憾的是,OpenFoam的原生开发系统是Linux,因 ...

  8. Python3中的字符串函数学习总结

    这篇文章主要介绍了Python3中的字符串函数学习总结,本文讲解了格式化类方法.查找 & 替换类方法.拆分 & 组合类方法等内容,需要的朋友可以参考下. Sequence Types ...

  9. Android(2)—Mono For Android App版本自动更新

    0.前言 公司Android查询的项目已经开始,整体采用Java后台+App前台来实现,由于项目小,App这块就我一个人开发,首先需要研究的是:Android版本的更新升级问题:本人经过近一周的学习整 ...

  10. 如何使用RobotFramework编写好的测试用例

    如何使用Robot Framework编写优秀的测试用例 概述 命名 测试套件命名 测试用例命名 关键字命名 setup和teardown的命名 文档 测试套件文档 测试用例文档 用户关键字文档 测试 ...