UIView 的基本介绍
概览
类定义了一个在屏幕上的矩形区域,并管理了这个区域界面上的内容。在运行的时候,View 对象处理矩形区域中任何内容渲染和任何内容的交互。这个 UIView 类提供了用背景颜色填充矩形区域的最基本的行为。通过 UIView的子类实现一些必要的渲染方法和事件处理方法,更多复杂的内容能够被显示出来。UIKit 框架也提供了一些标准的子类,范围从简单的button 到复杂的 tables,可以按照原样进行使用。例如: 一个 UILable 对象可以绘制文本 字符串,UIImageView 对象可以绘制一个image。
因为 view 对象是 app 进行用户交互的主要方式,View 对象就应该有更多的责任。这里只是其中一部分:
绘制 和 动画
- View 在矩形区域绘制内容使用的技术:UIKit, CoreGraphics 和 OpenGL ES。
- 一些 view 的属性可以动画的方式更新到新值。
布局 和 子视图管理
- 一个 view 可以包含 0 个或者多个子视图。
- 每个 view 都定义了在他们父视图总改变大小的行为。
- 一个视图可以定义子视图的尺寸和位置。
事件处理
- 一个 view 就是一个响应者,能够处理触摸事件和 UIResponder 类定义的另一些事件。
- view 能够使用
addGestureRecognizer:
方法去实例化一个手势识别器去处理普通的手势。
view 能够嵌入其他视图来实现复杂的视觉效果。通过嵌入操作,嵌入视图和被嵌入的视图之间会产生一种父子关系。通常情况下,一个子视图的可见区域是不会被切除的到父视图的范围内的。但是在 iOS 中你能够使用 clipsToBounds
属性去改变这个行为。一个父亲视图可以包含该很多个子视图,但是,每个子视图只有一个父视图。父视图是用来定位他的子视图。
view 的 frame, bounds 和 center 属性定义 view 的几何结构。 frame 定义的是 view 在父视图的坐标系中的位置和大小。frame 一般使用在调整 view 的位置和大小的布局过程中。center 属性可以用来在不改变视图大小的情况下调整视图的位置。bounds 属性定义的是视图内部的大小。bounds 大部分情况下是用在自定义的绘制代码中。frame 的 size 和 bounds 的 size 是耦合在一起的。矩形区域的 size 发生改变都会导致 frame 和 bounds 的 size 的改变。
关于怎么使用 UIView 类更加详细的信息,请查看View Programming Guide for iOS
创建视图
CGRect viewRect = CGRectMake(10, 10, 100, 100);
UIView* myView = [[UIView alloc] initWithFrame:viewRect];
这些代码是创建了一个 view ,view 在父视图的坐标中的位置是(10,10)。添加一个子视图到另外一个视图使用的是addSubview:
方法。在iOS 里面,姐妹视图是可以互相重叠并且不会有任何影响,从而进行合成复杂视图布置。 addSubview
方法会将指定的视图添加到姐妹视图的最上面。你可以通过调用 insertSubview:aboveSubview:
和 insertSubview:belowSubview:
方法指定子视图添加的 z-轴的次序关系。你也可以通过调用 exchangeSubviewAtIndex: withSubviewAtIndex:
方法来改变已经添加的两个子视图的位置。
当创建一个 View,会分配一个恰当的 autoresizingMask
属性值去确保 view 正确的设置尺寸。view 尺寸设置 主要 发生在 app 的界面的方向发生改变的时候。view 尺寸的设置也可能发生在其他的任何时候。例如,调用setNeedsLayout方法迫使你的视图更新它的布局。
视图的绘制周期
视图的绘制会在需要的时候发生。
- 当 view 第一次显示的时候, 系统会要求 view 绘制 view 上的内容。
- 在布局改变期间使视图全部或部分可见的时候, 系统会要求 view 绘制 view 上的内容。
视图包含该使用 UIKit 和 CoreGraphics 的自定义内容,系统会调用 view 的 drawRect:
方法。你实现这个方法去负责 view 内容的绘制到当前的图形上下文。这将创建一个 view 内容的静态视觉表现去被显示到屏幕上。
当 view 的实际内容发生改变的时候,你需要通知系统你的 view 需要重新绘制。你需要调用你视图的 setNeedsDisplay
或者 setNeedsDisplayInRect:
方法。这些方法会让系统知道在接下来的绘制周期中更新这个视图。因为会等待在下一个绘制期间根系视图,所以你可以在多个视图中调用这些方法来在同一时间更新视图。
Note
如果你正在使用 OpenGL ES 进行你的绘制,你应该使用
GLKView
来代替UIView
的子类。更多的关于怎样使用 OpenGL ES ,请看 OpenGL ES Programming Guide for iOS
关于 视图绘制周期 和 你的 view 在绘制周期中扮演的角色 的更加详细的信息请参考 View Programming Guide for iOS
动画
改变 View 的几个属性就可进行动画。改变属性创建一个动画,在很短的时间内将这些属性的改变传达给用户。UIView 类做了大量的工作去执行实际的动画,但是你仍然要明确那个属性的改变你想要变成动画。有两种不同的方式启动动画:
- 在 iOS4 之后,使用基于 block 的动画方法 (推荐的方式)
- 使用 begin/ commit 的动画方法。
基于 block 的动画的方式(例如 animateWithDuration:animations:
)大大简化了动画的创建。 调用这个方法你需要制定需要执行的动画和动画的选项(动画的设置)。 无论怎样,基于 block 的动画只能在 iOS4 之后才可用。如果你的 app 允许在 iOS4 之前的系统你必须使用
beginAnimations:context:
and commitAnimations
的类方法去标记你动画的开始和动画的结束。
下面这些属性是可以进行动画的:
- frame
- bounds
- center
- transform
- alpha
- backgroundColor
关于 动画配置的更加详细的信息请参考 View Programming Guide for iOS
线程问题
操作 app 用户界面必须在主线程中。因此,你总是在 app 的主线程中执行 UIView 类相关的代码。在唯一的时间里面,UIView 对象的创建这不是绝对的必要在主线程,但是 View 的其他操作必须在主线程中。
子类注意事项
对于视觉内容并且必须和用户进行交互,UIView 类是一个很关键的子类化的点。尽管有很多好的理由去子类化 UIView , 但是建议你这么做,只有最基本的 UIView 类和标准的系统视图不能提供你需要的功能的时候才对 UIView 进行子类化。子类化需要你做更多的工作去实现这个 view 并且优化 view 的性能。
有关如何避免子类化的信息,请参考 Alternatives to Subclassing
方法的重载
在进行子类化 UIView 的时候,有少量的方法你必须重载,大量的方法可能需要根据你的需要进行重载。因为 UIView 是一个高度可定制的类,不去重载可自定义的方法,同样也有很多方式去实现一个复杂的视图。同时,下面列表中的这些方法你可以考虑在你的 UIView 的子类中进行重载。
初始化
initWithFrame:
建议你去实现这个方法。 此外你也可实现自定义的初始化方法来替代这个方法。initWithCoder:
如果你的 view 是从一个 nib 文件加载的,并需要进行自定义的初始化操作。你需要实现这个方法。layerClass
只有你希望你的视图使用一个不同的 核心动画图层 作为一个根图层(存储图层)的时候 才需要使用这个属性。例如:你的视图需填充显示大的可滚动区域的时候,你能需要设置这个属性为CATiledLayer
类。
视图的绘制 和 印刷
drawRect:
你的视图需要绘制自定义内容的时候实现这个方法,如果不需要做任何的自定义绘制, 避免重载这个方法。drawRect:forViewPrintFormatter:
只要你需要在打印的过程中绘制你视图内容的差异化的东西,你需要实现这个方法。
- 约束
requiresConstraintBasedLayout
如果你的视图类需要需要约束恰当(正确)的去工作,你需要使用这个属性。updateConstraints
如果你的视图需要创建子视图之间的自定义约束,你需要实现这个方法。alignmentRectForFrame:
,frameForAlignmentRect:
你的视图需要进行排列的时候你需要实现这两个方法。
- 布局
sizeThatFits:
如果你需要你的视图有一个默认尺寸的时候,实现这个方法进行重新设置尺寸的操作。例如: 你可以使用这个方法来避免你的视图收缩到你子视图不能被正确显示的点去。layoutSubviews
如果你需要对子视图的约束,自动设置尺寸提供更加精切的布局控制的时候你需要实现这个方法。didAddSubview:
,willRemoveSubview:
当需要追踪子视图的添加和移除的时候需要实现这两个方法。willMoveToSuperview:
,didMoveToSuperview
需要追踪当前视图在你的视图层次中的运动的时候需要实现这两个方法。willMoveToWindow:
,didMoveToWindow
需要追踪你的视图运动到一个不同的 window 的时候。你需要实现这个方法。
事件处理
touchesBegan:withEvent:
,touchesMoved:withEvent:
,touchesEnded:withEvent:
,touchesCancelled:withEvent:
如果你需要立即的处理触摸事件,你需要实现这四个方法。 (基于手势的事件输出,使用手势识别器)gestureRecognizerShouldBegin:
如果你需要立即处理触摸事件,并可能需要防止粘附手势触发额外的事件的时候,需要实现这个方法。
替代子类化
很多视图行为在不需要子类化的情况下进行配置。在你进行重载这些方法之前,你需要考虑视图需要修改下列属性或行为来提供你自己需要的行为。
addConstraint(_:)
定义了 view 和 subview 的自动布局行为。autoresizingMask
当父视图的 frame 发生改变的时候,提供了自动布局行为。这个行为会和约束进行结合。contentMode
相对于 view 的 frame 为 view 的内容提供布局行为。这个属性会影响内容进行缩放去填充 view ,和 view 是否进行缓存和重绘制。
isHidden
oralpha
整体的改变 view 的透明度。而不是去隐藏或提供一个 alpha 去呈现你视图的内容。backgroundColor
设置视图的颜色,而不是绘图颜色自己。- Subviews 不是使用
drawRect:
方法去绘制你的内容,嵌入 image 和 label 的子视图去展示你想要的内容。 - Gesture recognizers 不是子类自己来拦截和处理触摸事件, 你可以使用 手势识别器 去发送一个 Target-action 到这个目标对象。
- Animations 使用内置的动画,而不是自己去实现动画。这个动画通过核心动画的支持是简单和快速和容易使用的。
- Image-based backgrounds view 只是用来显示相对静态的内容,可以考虑使用 UIImageView 对象加手势识别器 去替代 子类化和 image 的绘制操作。或者,你也可以使用一个通用的UIView对象,并指定你的image 作为视图的CALayer对象的 content 的内容。
动画是另一种方式去改变可以改变视图,而不需要子类化去实现复杂的绘制代码。 UIView 的很多属性是可以动画的。那么意味着,改变这些属性可以出发系统的动画。启动动画需要简短的代码去表明那些改变需要进行动画。
更多的 外观和行为配置 参考: UIKit User Interface Catalog