iPhone开发应用中的UIView你了解多少?继续我们上篇iPhone开发应用之UIView开发流程上篇的内容开始讲述。曾经有人这么说过,在iphone里你看到的,摸到的,都是UIView,所以UIView在iphone开发里具有非常重要的作用。
坐标系统转换矩阵
坐标系统转换矩阵给改变视图(或者是它的视图)提供了一个轻松和简易的方法。一个仿射转换是一个数学矩阵,它指定了在坐标系统中的点是怎么被映射到另一个坐标系统中的点。你可以对整个视图应用仿射转换,以基于其父视图来改变视图的尺寸,位置或者朝向。你也可以在你的绘制代码中应用仿射转换,以对已解释内容的独立部分实现相同的操控。如何应用仿射转换是基于这样的上下文的:
为了修改整个视图,可以修改视图transform属性的仿射转换值。
为了在视图中的drawRect:方法中修改内容的指定部分,可以修改与当前图形上下文相关的仿射转换。
当你想实现动画时,通常可以修改视图的transform属性值。例如,你可以使用这个属性来制作一个视图围绕中心点翻转的动画。你不应该在其父视图的坐标空间中用这个属性来永久的改变你的视图,像修改它的位置和尺寸。对于这种类型的改变,你可以修改视图的frame矩形。
注意:当修改视图的transform属性值时,所有的转换都是基于视图的中心点来实现的。
在视图的drawRect:方法中,你可以使用仿射转换来定位或者翻转你想要绘制的项目。相对于在视图某些部位中修正对象的位置,我们更倾向于相对于一个固定点去创建对象,通常是(0, 0),同时在绘制之前使用转换来定位对象。这样的话,如果在视图中对象的位置改变了,你要做的只是修改转换矩阵,这样比为对象重新创建新的位置性能更好开销更低。你可以通过使用CGContextGetCTM方法来获取关于图形上下文的仿射转换,同时可以用Core Graphics的相关方法在绘制中来设置或者修改这个转换矩阵。
当前转换矩阵(CTM)是一个在任何时候都被使用的仿射矩阵。当操控整个视图的几何结构时,CTM就是视图transform属性的值。在drawRect:方法中,CTM是关于图形上下文的仿射矩阵。
每个子视图的坐标系统都是构建在其祖先的坐标系统之上的。所以当你修改一个视图的transform属性,这个改变会影响到视图及其所有的子视图。然而,这些改变只会影响到屏幕上视图的最终解释。因为每个视图都负责绘制自己的内容和对自己的子视图进行布局,所以在绘制和布局的过程中它可以忽略父视图的转换。
Figure1- 6描述了在解释的时候,两个不同的转换因子是如何在视觉上组合起来的。在视图的drawRect:方法中,对一个形状应用一个45度的转换因子会使该形状翻转指定的角度。另外加上一个45度的转换因子会导致整个形状翻转90度。这个形状对于绘制它的视图来讲仍然只是翻转了45度,但是视图自己的转换让它看起来像使翻转了90度。
Figure 1-6 翻转一个视图和它的内容
重要:如果一个视图的transform属性不是其定义时转换矩阵,那么视图的frame属性是未定义的而且必须被忽略。当对视图应用转换时,你必须使用视图的bounds和center属性来获取视图的位置和尺寸。子视图的frame矩形仍然是有效的,因为它们与视图的bounds相关。
获取更多关于在运行时修改视图的transform属性,查看 “Translating, Scaling, and Rotating Views.”获取更多如何在绘制过程中使用转换来定位内容,查看 Drawing and Printing Guide for iOS.
点与像素
在iOS中,所有的坐标值和距离都被指定为使用浮点数,其单元值称为点。点的数量随着设备的不同而不同,而且彼此不相关。要明白关于点的最主要一点是它们提供了一个绘制用的固定框架。
Table 1-1 列出了不同iOS设备的分辨率(点度量)。前为宽后为长。只要你依照这些屏幕的尺寸来设计用户界面,你的视图就回被相应的设备正确显示。
Table 1-1
每一种使用基于点度量系统的设备都定义了一个用户坐标空间。这是几乎在你所有的代码都会用到的标准坐标空间。例如,当你要操控视图的几何结构或者调用Core Graphics方法来绘制内容时会用到点和用户坐标空间。即使有时用户坐标空间里的坐标时直接映射到设备屏幕的像素,你还是永远不应该假设这是永远不变的。相反,你应该记住:一个点并不一定对应着屏幕上的一个像素
在设备层面,所有由你指定的视图上的坐标在某些点上必须被转化成像素。然而,从用户坐标空间上的点到设备坐标空间上的像素通常由系统来处理。UIKit和Core Graphics都主要使用基于向量的绘制模型,所有的坐标值都被指定为使用点。这样,如果你用Core Graphics画了一条曲线,你会用一些值来指定这条曲线,而不管底层屏幕使用怎样的解决方法。
当你需要处理图像或者其他基于像素的技术,像OpenGL ES时,iOS会帮你管理这些像素。对于存储为应用程序的束中的资源的静态图像文件,iOS定义了一些约定,可以指定不同像素密度的图像,也可以在加载图像时最大限度的适应当前屏幕的解决方案。视图也提供了关于当前放缩因子的信息,以便你可以适当的调整任何基于像素的绘制代码来适应有更高级解决方案的屏幕。在不同屏幕的解决方案中处理基于像素内容的技术可以在"Supporting High-Resolution Screens"和"Drawing and Printing Guide for iOS"找到描述。
视图的运行时交互模型
当用户和界面进行交互时,或者由代码程序性的改变一些东西时,一系列复杂的事件就会发生在UIKit的内部来处理这些交互。在这个系列中的某些点,UIKit唤出你的视图类,同时给它们一个机会去响应程序的行为。理解这些唤出点对于理解视图在哪里融入系统很重要。Figure 1-7 展示了这些事件的基本序列,从用户触屏开始到图形系统更新屏幕内容来响应结束。同样的事件序列也会发生在任何程序性启动的动作。
Figure 1-7 UIKit 与视图对象进行交互
以下的步骤分解了图1-7中的事件序列,既解释了在每一步发生了什么,也解释了应用如何响应
1、用户触屏
2、硬件报告触摸事件给UIKit框架
3、UIKit框架将触摸事件打包成UIEvent对象,同时分发给适合的视图。(对于UIKit框架如何提交事件给视图的详细解释,查看 Event Handing Guide for iOS)
4、视图中的事件处理代码可能进行以下的动作来响应:
改变视图或者其子视图的属性(frame, bounds, alpha, 等等)
调用setNeedsLayout方法以标记该视图(或者它的子视图)为需要进行布局更新
调用setNeedsDisplay或者setNeedsDisplayInRect:方法以标记该视图(或者它的子视图)需要进行重画
通知一个控制器关于一些数据的更新
当然,哪些事情要做,哪些方法要被调用是由视图来决定的。
5、如果一个视图的几何结构改变了,UIKit会根据以下几条规则来更新它的子视图:
a、如果自动重设尺寸的规则在发生作用,UIKit会根据这些规则来调整视图。获取更多关于自动重设尺寸规则如何工作,查看
- "Handling Layout Changes Automatically Using Autoresizing Rules."
b、如果视图实现了layoutSubviews方法,UIKit会调用它。你可以在你的定制视图中覆盖这个方法同时用它来调整任何子视图的位置和大小。例如,一个提供了巨大滚动区域的视图会需要使用几个子视图作为“瓦块”而不是创建一个不太可能放进内存的巨大视图。在这个方法的实现中,视图会隐藏任何屏幕外的子视图,或者重定位它们然后用来绘制新的可视内容。作为这个流程的一部分,视图的布局代码也可以废止任何需要被重画的视图。
6、如果任何视图的任何部分被标记为需要重画,UIKit会要求视图重画自身。
对于显式的定义了drawRect:方法的定制视图,UIKit会调用这个方法。这方法的实现应该尽快重画视图的指定区域,并且不应该再做其他事。不要在这个点上做额外的布局,也不要改变应用的数据模型。提供这个方法仅仅是为了更新视图的可视内容。
标准的系统视图通常不会实现drawRect:方法,但是也会在这个时候管理它们的绘制。
7、任何已经更新的视图会与应用余下的可视内容组合在一起,同时被发送到图形硬件去显示。
8、图形硬件将已解释内容转化到屏幕上。
注意:上面的更新模型主要应用于使用标准系统视图和绘制技术的应用。使用OpenGL ES来绘制的应用通常会配置一个单一的全屏视图和直接绘制相关的OpenGL图像上下文。你的视图还是应该处理触屏事件,但是它是全屏的,毋需给子视图布局或者实现drawRect:方法。获取更多关于使用OpenGL ES的信息,查看 OpenGL ES Programming Guide for iOS.
给定之前的一系列步骤,将自己的定制视图整合进去的方法包括:
事件处理方法:
- touchesBegan:withEvent:
- touchesMoved:withEvent:
- touchesEnded:withEvent:
- touchesCancelled:withEvent:
- layoutSubviews方法
- drawRect:方法
这些是视图的最常用的覆盖方法,但是你可能不需要覆盖全部。如果你使用手势识别来处理事件,你不需要覆盖事件处理方法。相似的,如果你的视图没有包含子视图或者它的尺寸不会改变,那就没有理由去覆盖layoutSubviews方法。最后,只有当视图内容会在运行时改变,同时你要用UIKit或者Core Graphics等本地技术来绘制时才需要用到drawRect。
要记住这些是主要的整合点,但是不仅仅只有这些。UIView类中有些方法是专门设计来给子类覆盖的。你应该到UIView Class Reference中查看这些方法的描述,以便在定制时清楚哪些方法适合给你覆盖。
有效使用视图的提示
当你需要绘制一些标准系统视图不能提供的内容时,定制视图是很有用的。但是你要负责保证视图的性能要足够的高。UIKit会尽可能的优化视图相关的行为,也会帮助你提高性能。然而,考虑一些提示可以帮助到UIKit。
重要:在调整绘制代码之前,你应该一直收集与你视图当前性能有关的数据。估量当前性能让你可以确定是否真的有问题,同时如果真的有问题,它也提供一个基线,让你在未来的优化中可以比较。
视图不会总是有一个相应的视图控制器
在应用中,视图和视图控制器之间的一对一关系是很少见的。视图控制器的工作是管理一个视图层次,而视图层次经常是包含了多个视图,它们都有自包含特性。对于iPhone应用,每个视图层次通常都填满了整个屏幕,尽管对于iPad应用来说不是。
当你设计用户界面的时候,考虑到视图控制器的所扮演的角色是很重要的。视图控制器提供了很多重要的行为,像协调视图的展示,协调视图的剔除,释放内存以响应低内存警告,还有翻转视图以响应界面的方向变更。逃避这些行为会导致应用发生错误。
获取更多关于视图控制器的信息,查看 View Controller Programming Guide for iOS
最小化定制的绘画
尽管定制的绘画有时是需要的,但是你也应该尽量避免它。真正需要定制绘画的时候是已有的视图类无法提供足够的表现和能力时。任何时候你的内容都应该可以被组装到其他视图,最好结果时组合那些视图对象到定制的视图层次
利用内容模式
内容模式可以最小化重画视图要花费的时间。默认的,视图使用UIViewContentModeScaleToFill 内容模式,这个模式会放缩视图的已有内容来填充视图的frame矩形。需要时你可以改变这个模式来调整你的内容,但是应该避免使用UIViewContentModeRedraw内容模式。不管哪个内容模式发生作用,你都可以调用setNeedsDisplay或者setNeedsDisplayInRect:方法来强制视图重画它的内容。
可能的话将视图声明为不透明
UIKit使用opaque属性来决定它是否可以优化组合操作。将一个定制视图的这个属性设置为YES会告诉UIKit不需要解释任何在该视图后的内容。这样可以为你的绘制代码提高性能并且是推荐的。当然,如果你将这个属性设置为YES,你的视图一定要用不透明的内容完全填充它的bounds矩形。
滚动时调整视图的绘制行为
滚动会导致数个视图在短时间内更新。如果视图的绘制代码没有被适当的调整,滚动的性能会非常的缓慢。相对于总是保证视图内容的平庸,我们更倾向于考虑滚动操作开始时改变视图行为。例如,你可以暂时减少已解释的内容,或者在滚动的时候改变内容模式。当滚动停止时,你可以将视图返回到前一状态,同时需要时更新内容。
不要嵌入子视图来定制控制
尽管在技术上增加子视图到标准系统控制对象-继承自UIControl的类-是可行的,你还是永远不应该用这种方法来定制它们。控制对象支持定制,它们有显式并且良好归档的接口。例如,UIButton类包含了设置标题和背景图片的方法。使用已定义好的定制点意味着你的代码总是会正确的工作。不用这些方法,而嵌入一个定制的图像视图或者标签到按钮中去会导致应用出现未预期的结果。
小结:关于iPhone开发应用之UIView开发流程 下篇的内容介绍完了,希望本文对你有所帮助!如果你对iphone开发很有兴趣的话,请看iphone开发频道,更多文章可以让你参考。