提高绘画性能
绘图是比较耗性能的, 这对任何平台都是这样. 优化代码性能是开发当中重要的一环. 表A-1列举了几个优化绘图代码的几个建议, 你应该使用性能工具来测试你的代码, 并移除污点代码和冗余代码.
表A-1 几个关于绘图性能的提示
Tip | Action |
---|---|
较少绘制 | 在每次界面刷新周期内, 你应该只更新需要重绘的部分. 如果你使用drawRect: 方法来绘制view, 那么应该制定更新特定rect. 如果使用OpenGL, 那么你必须自己去跟踪更新操作. |
谨慎地调用setNeedsDisplay: 方法 |
调用setNeedsDisplay 方法是需要花费时间和资源去计算实际需要更新的区域. 不要传一个包含整个view的rect参数进去. 也不要没事就调用该方法, 而是按需调用. |
尽可能多地将view设置为不透明(opaque==YES) | 当view时不透明时, 组合多个view的消耗比view透明时少的多. 所以当不需要透明的view时, 尽量将view设置为不透明. |
当滚动视图时,复用其中的cell或内容view | 当滚动视图时, 重复创建新的view会到时滚动不平滑, 会卡顿. |
通过修改transformation matrix来复用path | 通过修改当前的变换矩阵, 你可以使用同一个path来绘制不同内容. |
在滚动时, 避免清除之前的内容 | 默认情况下, UIKit在调用drawRect: 方法进行重绘时,会将之前的内容清除掉. 如果某个view要响应滚动事件, 那么该view会在滚动更新期间重复清除同一区域, 这是比较耗性能的. 所以你可以通过将属性clearsContextBeforeDrawing 设置为NO,来禁用该行为. |
在绘图时减少改变绘图状态(graphic state)的次数 | 修改graphic state需要底层绘图子系统来完成. 如果你的好几个内容需要相同的graphic state的话, 尽量将它们放在一块完成, 避免多出改变graphic state |
使用Instrument工具来调试你的代码性能 | Core Animation instrument工具能够帮助发现代码中的性能问题, 尤其是: ①Flash Updated region可以让你很容易的知道你是视图的那部分内容需要更新 ②色彩错位图像可以帮你看到排列不好的图片, 这会导致模糊图片和较差的性能. 更多内容请看Instruments User Guide的Measuring Graphics Performance in Your iOS Device部分 |
让视图支持高分率屏幕
在iOS4.0后, 屏幕就分Retina(高分辨率)和普通. 为了适配高分辨率屏幕, 大部分工作有系统完成, 你需要的事情比较少, 比如适配基于光栅化的图片等.
为支持高分辨率的工作清单
为了支持高分辨率屏幕, 你需要干如下事情:
- 提供高分辨率的版本的图片
- 提供高分辨率版本的icon
- 对于基于矢量的形状和内容, 请继续使用自定义Core Graphic和UIKit绘图代码。如果您想为绘制的内容添加额外的东西, 请参关于iOS绘图的一些概念中
点(point) VS 像素(pixel)
的内容,以了解如何做到这一点。 - 如果使用OpenGL ES进行绘图,请决定是否要选择高分辨率绘图,并相应地设置layer的比例因子(scale)
- 对于您创建的自定义图像,修改图像创建代码以考虑当前的比例因子
- 如果应用程序使用核心动画,根据需要调整代码以补偿scale差异
充分利用iOS平台优势
iOS为绘图提供了多种技术支持, 来帮助你打造更好的APP, 而不需要考虑屏幕的分辨率:
- 标准的UIKit视图(文本框, 按钮, TableView等)在各种分辨率下都能很好的显示
- 基于矢量的内容(UIBezierPath, CGPathRef, PDF)会自动利用多余像素来清晰的渲染线条.
- 文本也会自动利用高分辨率来清晰显示.
- UIKit能够自动为你的屏幕适配各种图片(@2x, @3x)
如果你的APP使用原生绘图技术来进行渲染, 那么你只需提供高分辨率版本的图片即可适配高分辨率屏幕.
使用OpenGL ES或者GLKit技术在高分辨率屏幕上绘制
如果APP使用OpenGL ES或者GLKit技术来绘制内容, 那么之前的绘图代码在不需要改动的情况, 应该能够继续工作. 当你在高分辨率屏幕上绘制内容时, 你的内容会进行收缩从而变得块状化. 之所以会这样, 是因为底层支持OpenGL的是CAEAGLLayer
类, 该类的默认行为与Core Animation类似. 也就是说, scale factor默认设置为1.0, 这会使得Core Animation合成器压缩内容, 从而使得在高分辨率屏幕上显示的块状化. 为了避免块状化, 你需要手动增加OpenGL渲染缓冲区(renderbuffers)的大小以匹配屏幕(有多更多像素, 内容可的绘制可以更加细节化)大小. 因为想渲染缓冲区添加更多像素会影响性能, 你必须显示的选择支持高分辨率屏幕.
如若要启用高分辨率绘图, 那么必须手动改变view的scale来显示OpenGL或GLKit绘制的内容. 将view的contentScaleFactor
属性从1.0到2.0, 将触发对底层CAEAGLLayer
对象改变scale. renderbufferStorage:fromDrawable:
方法用于将layer对象绑定到renderbuffers
中, 通过将scale来同比缩放layer的bounds后, 得出渲染缓冲区的size. 因此, 将比例因子加倍(scale), 那么渲染缓冲区的大小也需要加倍, 从而能够容纳更多的像素. 之后将使用这些额外的像素来绘制内容.
代码B-1显示了将layer绑定到渲染缓冲区的正确姿势(方法). 如果你创建的是OpenGL ES类型APP, 那么这个步骤xcode已经帮你完成了. 那么你只需为view设置合适的scale就行. 如果不是, 那么就需要使用如下代码来获取渲染缓冲区的size. 对于给定类型的设备, 您不应该假定渲染缓冲区大小是固定的.
代码清单B-1 初始化渲染缓冲区, 并检索其实际尺寸
GLuint colorRenderbuffer;
glGenRenderbuffersOES(1, &colorRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer);
[myContext renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:myEAGLLayer];
// Get the renderbuffer size.
GLint width;
GLint height;
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &width);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &height);
重要: 底层有
CAEAGLLayer
支持的view不应该实现自定义的drawRect:
方法. 实现了drawRect:
方法会使得系统更改scale以便适配屏幕的scale. 如果你的绘图代码不希望出现这种情况, 那么APP的内容将会渲染的不正确
图片加载
出于功能和美学的原因,图像是App用户界面的普遍元素。它们可能是应用程序的一个关键区别因素。APP的许多地方需要用到image, 从APP icon,到launchImage, 还有按钮, 背景等等.
此外,IOS支持使用UIKit和Core Graphic框架来加载和显示图像。确定使用哪个框架和函数取决于如何使用它们。不过,只要有可能,建议使用UIKit类来展示代码中的图像。表C-1列出了一些使用场景和处理它们的推荐选项。
表C-1 image的使用场景
场景 | 推荐使用方法 |
---|---|
image作为view的内容呈现 | 使用类UIImageView 来展示image. 假设你view的唯一内容时image, 那么你仍然可在imageView的上面添加其他view, 比如控制或其他内容 |
image作为view的装饰,并成为view的一部分 | 使用UIImage 来加载image并绘制到view中 |
将bitmap数据保存到image对象中 | 可以使用UIKit或Core Graphic函数来操作, 前面知识有覆盖 |
将image保存为JPEG或PNG格式 | 使用image的原始数据创建一个UIImage 对象. 使用UIImageJPEGRepresentation 或UIImagePNGRepresentation 函数来获取NSData对象, 然后是NSData实例方法将数据保存到文件中. |
系统支持的图片
UIKit框架以及iOS的底层系统框架为您提供了创建、访问、绘制、写入和操作图像的广泛可能性。
UIKit中的Image类和函数
UIKit框架有三个类和一个protocol用来进行image相关的操作:
UIImage
该类表示UIKit中的图像. 你可以从不同的source创建UIImage对象, 保存file和Quartz图像. 该类的类方法可以让你使用不同的blend mode和opacity来将图像绘制到当前绘图上下文中.
UIImage类会自动为你处理相应的适配操作, 比如scale(考虑到高分辨率屏幕的显示). 并且在使用Quartz图像是, 你需要手动修改坐标系.
UIImageView
该类的实例是显示单个图像或动画展示一系列图像的view。如果图像是view中的唯一内容,则使用UIImageView类而不是绘制图像。
UIImagePickerController
和UIImagePickerControllerDelegate
该类可以让APP访问用户相册中的图片和电影, 或者拍摄一张照片. 该类负责present一个界面, 让用户可以从相册中选择照片. 当用户选择了一张照片, 它会将照片以一个UIImage
实例传给delegate, 该delegate必须实现protocol中的方法.
除了这些类, UIKit还声明了多个函数用来执行不同的image相关的任务:
- 获取bitmap图像
UIGraphicsBeginImageContext
函数可以创建一个离屏的bitmap图像上下文. 你可以在该上下文中绘制内容, 在从上下文中获取UIImage对象. - 获取或者缓存image数据 每个UIIMage对象背后是一个
CGImageRef
对象在支撑, 并可以直接访问这个背后的对象. 然后,可以将core graphic对象传递给图像I/O框架来保存数据。还可以通过调用UIImagePNGRepresentation
或UIImageJPEGRepresentation
函数将UIImage对象中的图像数据转换为PNG或JPEG格式。然后,可以访问数据对象中的字节,并且可以将图像数据写入文件。 - 将image写入相册中 调用
UIImageWriteToSavePhotosAlbum
函数, 并传入UIImage对象, 可以将图像写入相册.
其他和图片相关的框架
除了UIKit之外,还可以使用多个系统框架来创建、访问、修改和写入图像。如果您发现无法使用UIKit方法或函数完成某个与图像相关的任务,那么这些底层框架之一的函数可能能够执行您想要的操作。这些函数中的一些可能需要一个CoreGraphic图像对象(CGImageRef)。您可以通过CGImage
属性访问底层支持UIImage对象的CGImageRef对象。
注意:如果存在UIKit方法或函数来完成给定的与图像相关的任务,则应该使用UIKit,而不是使用任何相应的底层函数
Quartz中的Core Graphic框架是最重要的底层系统库. 该框架的一些函数和UIKit中的方法和函数一一对应; 比如, 有些CoreGraphic函数可以让你创建或绘制bitmap, 而其他可以让你从不同的source创建image. CoreGraphic还提供了UIKit中没有的功能, 比如已存在图像中选取部分来创建图片, 使用色彩空间, 获取其他有趣的图片特征信息, 比如每行多少字节, 一个点又多少像素, 和渲染意图等等.
图像I/O框架与CoreGraphic密切相关。它允许一个应用程序读写的大多数图像文件格式,包括标准的网络格式,高动态范围图像,和原始相机数据。它具有快速的图像编码和解码、图像元数据和图像缓存特征。
资产库(Assets Library)是一个允许APP访问照片APP资产管理的框架。您可以通过representation(例如PNG或JPEG)或URL获得资产(asset)。通过representation或URL,您可以获得CoreGraphic图像对象或原始图像数据。该框架还允许您将图片写入保存的照片相册
系统支持的图片格式
表C-2列举了iOS所支持的图片格式, png格式iOS最喜欢的. 通常UIKit支持的格式, 其他图像I/O框架也支持.
表C-2 iOS支持的图片格式
格式 | 扩展名 |
---|---|
便携式网络图形(PNG) | .png |
标记图像文件格式(TIFF) | .tiff或.tif |
联合摄影专家组(JPEG) | .jpeg 或 .jpg |
图形交互格式(GIF) | .gif |
窗口图片格式 | .ico |
窗口光标 | .cur |
XWindow位图 | .xbm |
维持图片的质量
为您的用户界面提供高质量的图像应该是您的设计中的优先考虑的事情。图像提供了一种合理有效的方式来显示复杂的图形,并且应该在适当的地方使用。在为应用程序创建图像时,请记住以下指南:
- 使用png格式的图片 png格式提供了无损的图像内容, 这意味着将图像数据格式保存为png, 然后将其读回会得到前后一致的图像. png还是优化的存储格式, 用于更快的读取图像数据, 它是iOS的首选图像格式.
- 创建Image以使不需要重新调整大小 如果计划使用特定大小的图片, 请确保创建相应大小的的图片资源. 不要创建更大的图像, 然后在缩小. 因为缩放需要消耗CPU资源. 如果显示的图像需要改变大小, 那么请求创建多个版本(大小不一)的图片, 并从相对接近目标大小的地方缩放大小.
- 从不透明的png文件中删除alpha通道 如果png图像的每个像素都是不透明的, 那么删除alpha通道避免图片混合后的合成步骤. 这样就简化了显示, 提高了性能.