iOS是怎么"绘画"的?
什么是绘图引擎
如果您以前从事其它平台的图形/界面开发或者游戏开发,一定知道, 不管上层UI怎么呈现和响应, 底层必须有一个绘图引擎. iOS也不例外. 本文详细介绍了iOS Graphics的用法和相关知识, 希望对您的Coding有帮助.
- 博客: http://www.cnblogs.com/jhzhu
- 邮箱: jhzhuustc@gmail.com
- 作者: 知明所以
- 时间: 2013-12-31
^此博客需要对CALayer
和UIView
有基本的了解. 可参考博客谈谈iOS Animation
什么是绘图引擎
绘图引擎, 通俗来说就好比给你一张纸一支笔和若干颜色的颜料, 你可以用它来做最基本的图形绘制.
一个最基本的绘图引擎包括一下接口:
//Code-1
I1. drawLine() //绘制任意线条,并支持对线条上色.
I2. drawPath() //根据路径绘制形状,并支持填充颜色.
I3. drawImage() //绘制图像(e.g. xxx.jpg, xxx.png)
I4. drawGradient() //绘制渐变填充
I5. transform() //矩阵映射变换.
I6. drawText() //绘制文字
不难想象, 有了以上接口, 我们就可以方便的绘制任意想要的图像.
这里强调的是方便, 有些接口并不是必须的. 比如说drawImage()
,我们总可以调用有限次drawLine()
和drawShape()
来绘制任意给定的Image. 但是复杂程度可想而知.
一个绘图引擎设计的目的就是为了方便上层调用, 所以它会封装一些最常用和最基本的接口. 以上5个接口就满足这两个条件之一. 所谓最常用和最基本并没有一个明确的定义, 所以不同的绘图引擎可能会多一些常用接口,但都大同小异.
iOS的绘图引擎
下面我们就Code-1
里提到的接口在iOS平台上做一个介绍.
在哪里绘制?
如果我们在XCode里新建一个UIView
类, 我们会得到以下代码:
//Code-2
#import "GraphicsViewControllerView.h"
@implementation GraphicsViewControllerView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code }
return self;
}
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
@end
通常,drawRect()
都会被注释起来. 因为, 如果你向UIView
添加subView
或者设置UIView
的显示相关的属性(e.g. backgroundCrolor
)的时候, UIKit
会自动的把这些参数代表的含义绘制到CALayer
上去. 也就是说, 一般情况我们并不需要自己来绘制, UIKit
会自动帮我们完成绘制工作.
但是, 当不添加subView
, 不设置UIView
的显示相关的属性时, 我们就可以通过重载drawRect()
来手动绘制图像了.
Context
A graphical context can be thought of as a canvas, offering an enormous number of properties such as pen color, pen thickness, etc. Given the context, you can start painting straight away inside the drawRect: method, and Cocoa Touch will make sure that the attributes and properties of the context are applied to your drawings. We will talk about this more later, but now, let’s move on to more interesting subjects.
drawText
我们新建一个UIView
类CustomUIView
,如下重载drawRect()
方法.
新建CustomUIView
的对象,不设置任何属性,添加到显示列表.
//Code-3
- (void)drawRect:(CGRect)rect{
UIFont *font = [UIFont systemFontWithSize:40.f];
NSString *myString = @"Some String";
[myString drawAtPoint:CGPointMake(40, 180) withFont:font];
}
运行, 就可以看到我们没有添加任何UITextField
却显示了文字~
more:
关于文字绘制的方法还有 drawInRect:withFont:
等几个方法, 可参考官方文档.
setColor
我们把Code-3
中的代码添加两行, 变成:
//Code-4
- (void)drawRect:(CGRect)rect{
UIColor* color = [UIColor blueColor]; //create color
[color set]; //set color
UIFont *font = [UIFont systemFontWithSize:40.f];
NSString *myString = @"Some String";
[myString drawAtPoint:CGPointMake(40, 180) withFont:font];
}
就可以看到,文字由黑色变成了蓝色.
more:
UIColor
还有两个方法setStroke
和setFill
分别设置线条颜色和填充颜色. 在后面的章节会用到.set
方法影响所有前景色.
drawImage
同样的, 如下重写drawRect
方法:
//Code-5
- (void)drawRect:(CGRect)rect{
/* Assuming the image is in your app bundle and we can load it */
UIImage *xcodeIcon = [UIImage imageNamed:@"filename.png"];
[xcodeIcon drawAtPoint:CGPointMake(0.0f, 20.0f)];
}
我们看到图像被绘制出来了.
more:
UIImage
还有其他绘制方法:
drawInRect:
drawAsPatternInRect:
drawAtPoint:blendMode:alpha:
drawInRect:blendMode:alpha:
方法名已经很清楚的说明了方法的用途. 具体可参考官方文档
drawLine
这两个是绘图引擎里最基本的, 所以放在一起讲述.
//Code-6: drawLine
- (void)drawRect:(CGRect)rect{
/* Step1 设置绘图颜色 */
[[UIColor brownColor] set];
/* Step2 获取当期的画布: Graphic Context */
CGContextRef currentContext = UIGraphicsGetCurrentContext();
/* Step3 设置线条宽度 */
CGContextSetLineWidth(currentContext,5.0f);
/* Step4 把画笔移动到起始点 */
CGContextMoveToPoint(currentContext,50.0f, 10.0f);
/* Step5 从起始点绘制线条到终点 */
CGContextAddLineToPoint(currentContext,100.0f, 200.0f);
/* Step6 提交绘制 */
CGContextStrokePath(currentContext);
}
如果想连续绘制多条线, 可以再Code-6
中的Step5
和Step6
之间多次调用CGContextAddLineToPoint()
.
more:
CGContextSetLineJoin
可以改变线条交叉点的样式.
drawPath
如果我们想快速绘制一条折线, 调用drawLine
就显得有些臃肿. 所以有了drawPath
, 它是drawLine
的加强版.
drawPath
的一般步骤如下:
//Code-7: drawPath
- (void)drawRect:(CGRect)rect{
/* Step1 获取当期的画布: Graphic Context */
CGContextRef currentContext = UIGraphicsGetCurrentContext();
/* Step2 创建 path */
CGMutablePathRef path = CGPathCreateMutable();
/* Step3 移动到起始点 */
CGPathMoveToPoint(path,NULL, screenBounds.size.width, screenBounds.origin.y);
/* Step4 绘制一个椭圆 */
CGPathAddEllipseInRect(path, &CGAffineTransformIdentity, CGRectMake(0, 320, 320, 160));
/* Step5 再添加一条直线 */
CGPathAddLineToPoint(path,NULL, screenBounds.origin.x, screenBounds.size.height);
/* Step6 向画布添加path */
CGContextAddPath(currentcontext, path);
/* Step7 设置绘制类型: kCGPathStroke(绘制边缘), kCGPathFill(填充path内区域), kCGPathFillStroke(包含前面两项)*/
CGContextDrawPath(currentcontext, kCGPathFillStroke);
/* Step8 提交绘制 */
CGContextStrokePath(currentContext);
/* Step9 release path */
CGPathRelease(path);
}
more:
常见几何图形的绘制接口: CGPathAddCurveToPoint
,CGPathAddArcToPoint
,CGPathAddRect
等等...
transform
iOS中的transform
使用CGAffineTransform
表示的. 你可以用矩阵方式构造任意二维变换:
/*
a: The value at position [1,1] in the matrix.
b: The value at position [1,2] in the matrix.
c: The value at position [2,1] in the matrix.
d: The value at position [2,2] in the matrix.
tx: The value at position [3,1] in the matrix.
ty: The value at position [3,2] in the matrix.
*/
CGAffineTransformMake(CGFloat a, CGFloat b, CGFloat c, CGFloat d, CGFloat tx, CGFloat ty)
矩阵表示为:
iOS也提供了常见变换的快速构建方式:
CGAffineTransformIdentity //单位矩阵, 不做任何变换
CGAffineTransformMakeRotation //旋转
CGAffineTransformMakeScale //缩放
CGAffineTransformMakeTranslation //平移
在Code-7 Step4
中, 绘制path
时用到了单位矩阵CGAffineTransformIdentity
, 表示不做任何变换. 通常情况下, 都是在绘制阶段把transform
作为参数传入. 上面提到的CGPathAddCurveToPoint
,CGPathAddArcToPoint
,CGPathAddRect
函数都有一个transform
参数.
iOS绘图在项目中的应用
通常, 我们只需要随心所欲的对UIView
增加subView
, UIKit
会自动帮我们绘制. 但是下列情况下可能需要手动绘制:
- 优化
UITableViewCell
的时候. 如果我们的cell
很复杂, 有很多subView
, 就会变得很卡顿. 就需要手动绘制了. 可参考博客: 优化UITableView性能或者IOS详解TableView——性能优化及手工绘制UITableViewCell - 暂未想到, 以后想到再说.
iOS是怎么"绘画"的?的更多相关文章
- ios 创建和绘画pdf文件 -转
转自:http://blog.csdn.net/ant1239/article/details/7761676 本方法为项目中画pdf的一个方法,画pdf,一共分为几步,1,获取地址,有两种获取地址方 ...
- 同一个页面多个CALayer重绘的办法
//知识点,CALayer的重绘,-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx 方法,CALayer的渐变色.多个CALa ...
- UISCREEN 和支持高分辨率的显示屏
UIScreen对象包含了整个屏幕的边界矩形.当构造应用的用户界面接口时,你应该使用该对象的属性来获得推荐的矩形大小,用以构造你的程序窗口. CGRect bound = [[UIScreen mai ...
- ios中图片的绘画和截图
ios中图片的绘画和截图 CGImageCreateWithImageInRect截图和UIGraphicsGetImageFromCurrentImageContext绘画图片 使用CGImageC ...
- iOS使用Core Graphics和UIBezierPath绘画
通过UIView的子类的- (void)drawRect:(CGRect)rect 函数可用对视图进行重新绘画: 要重新绘画可以通过Core Graphics和UIBezierPath来实现. 1.通 ...
- iOS: 使用CGContextRef,CGPath和UIBezierPath来绘画
这三种东西:CGContextRef,CGPath和UIBezierPath.本质上都是一样的,都是使用Quartz来绘画.只不过把绘图操作暴露在不同的API层面上,在具体实现上,当然也会有一些细小的 ...
- [ios]ios画线 使用CGContextRef,CGPath和UIBezierPath来绘画
参考 :http://www.mgenware.com/blog/?p=493 这三种东西:CGContextRef,CGPath和UIBezierPath.本质上都是一样的,都是使用Quartz来绘 ...
- 开源 iOS 项目分类索引大全 - 待整理
开源 iOS 项目分类索引大全 GitHub 上大概600个开源 iOS 项目的分类和介绍,对于你挑选和使用开源项目应该有帮助 系统基础库 Category/Util sstoolkit 一套Cate ...
- IOS 整体架构 和 MVC布局
IOS的生态系统 IOS生态系统不仅仅是指产品,更重要的是指 iPhone/iPad/iPod/Mac +iCloud+App整个系统,包括Siri (部分设备不支持).FaceTime.Safari ...
随机推荐
- Android sdk manager不能更新下载缓慢的解决方法
通常情况下,下载Android SDK需要连接谷歌的服务器进行下载,由于国内水深火热的网络,速度基本为0.好在国内也有一个更新的镜像地址.本文章介绍如何在不FQ的情况下,使用国内镜像地址,更新andr ...
- MSCRM 2015 新功能(一)
MS官网信息:http://www.microsoft.com/en-us/dynamics/crm-customer-center/what-s-new.aspx MS官网中提到的新功能主要以下几块 ...
- 无法将类型为“Excel.ApplicationClass”的 COM 对象强制转换为接口类 型“Excel._Application”。此操作失败的原因是对 IID 为“{000208D5 -0000-0000-C000-000000000046}”的接口的 COM 组件调用 QueryInterface 因以下错误而失败: 加载类型库/DLL 时出错。 (异常来 自 HRESULT:
无法将类型为“Excel.ApplicationClass”的 COM 对象强制转换为接口类 型“Excel._Application”.此操作失败的原因是对 IID 为“{000208D5 -000 ...
- R语言学习笔记:小试R环境
买了三本R语言的书,同时使用来学习R语言,粗略翻下来感觉第一本最好: <R语言编程艺术>The Art of R Programming <R语言初学者使用>A Beginne ...
- Android Java类编写规范+优化建议
本文仅是我个人在实际开发中习惯的编写方式,当然这种方式也是来自很多官方的推荐,所以在一定程度上是可以被模仿套用的.本文将不定期更新~ 零.指导原则 优先保证可读性,不要过分追求代码艺术和效率 在可读性 ...
- ListView的addHeaderView()方法相关问题
使用listView.addHeaderView(view) 可以在 listView 上方添加一个view视图 ,使listView和这个view连接在一起 效果上看上去是一个整体 一般用于上拉刷新 ...
- 高精度练习(hdoj1042)
Problem Description Given an integer N(0 ≤ N ≤ 10000), your task is to calculate N! Input One N in ...
- iOS设计模式之中介者模式
中介者模式 基本理解 中介者模式又叫做调停者模式,其实就是中间人或者调停者的意思. 尽管将一个系统分割成许多对象通常可以增加可复用性,但是对象之间的连接又降低了可复用性. 如果两个类不必彼此直接通信, ...
- OC数组排序
NSArray *array = @[@"tailong", @"kaersasi", @"airuiliya", @"yingl ...
- Swift 设计指南之 编程规范
基本准则 用法一目了然是你设计时最重要的目的. 方法和属性这样的实体只声明一次,却会被重复调用.因此你在设计 API 时应尽可能使其简单明了.当评估某个设计时,只阅读声明往往是不够的,有时还需要检查它 ...