UIView概述
UIView
是所有视图的根类,定义了所有视图的公共行为,属于 UIKit
框架。
UIView
是App界面的基本构件块,它在屏幕上定义了一个矩形区域,并处理该区域中的绘图和触摸事件,是用户和App的主要交互方式。
使用 UIView
可以执行如下操作:
绘制内容和动画:
UIView
使用Core Graphics
、OpenGL ES
、UIKit
之类的图形技术 在其矩形区域中绘制形状、图像和文本。而GLKView
使用的是 OpenGL ES进行绘制。布局和管理子视图:父View负责子View的位置和调整大小;管理子视图的列表
事件处理(Event Handling Guide for iOS):接收触摸事件;参与响应者链
支持拖放交互
对焦点变化做出反应
对视图的大小、位置和外观属性进行动画
UIView
与核心动画层(Core Animation layers
)一起工作,处理视图内容的渲染和动画。
UIKit
中每一个 UIView
由一个 CALayer
对象支持,该对象管理视图的后台存储(backing store
),并处理视图相关的动画。
我们执行的大多数操作都应该通过 UIView
接口进行。如果需要对 UIView
的渲染或动画行为进行更多控制,一般通过其图层执行操作。
用户交互式更新页面的过程:
1、用户触摸屏幕
2、硬件向UIKit框架报告触摸事件。
3、UIKit框架将touch打包到UIEvent对象中,并将其分派到合适的视图中。(Event Handling Guide for iOS)
4、视图的事件处理代码对事件作出响应。你的代码可能:
- 改变视图或其子视图的属性(frame、bounds、alpha等)。
- 调用
setNeedsLayout
方法将视图(或其子视图)标记为需要更新布局。 - 调用
setNeedsDisplay
或setNeedsDisplayInRect:
方法将视图(或其子视图)标记为需要重新绘制。 - 通知控制器某些数据的更改
5、如果视图的几何图形因任何原因发生更改,UIKit
将根据以下规则更新其子视图:
- 如果为视图配置了自动调整大小规则(
autoresizing rules
),UIKit
会根据这些规则调整每个视图。 - 如果视图实现
layoutSubviews
方法,UIKit
将调用它。
6、如果任何视图的任何部分被标记为需要重绘,UIKit会要求视图重绘自己。
对于显式定义drawRect:方法的自定义视图,UIKit调用那个方法。
7、任何更新的视图都将与应用程序的其他可见内容合成,并发送到图形硬件进行显示。
8、图形硬件将渲染的内容传输到屏幕。
CALayer 和 UIView 的关系
在iOS中,每一个
UIView
都有一个对应的CALayer
对象支持layer
为UIView
提供基础结构,图层layer
使视图内容的绘制 和 动画更加容易和有效,并在此过程中保持高帧率。CALayer
是UIView
内部实现细节。CALayer
不能处理事件、绘制内容、参与响应者链或执行许多其他操作,这就需要UIView
处理这些类型的交互。在
iOS
中,视图只是图层对象的薄薄包装。
因此使用轻量级的UIView
并没有显著的性能影响,这使得你既可以使用CALayer
底层的特性,也可以使用UIView
的高级API(如:自动布局、事件处理)。最好使用图层相关视图,而不是创建独立的图层关系,因为视图可以处理复杂的事件。
UIView
与 Core Animation layers
结合使用,以处理视图内容的渲染和动画处理。核心动画层对象的使用对性能具有重要意义。尽可能少地调用视图对象的实际绘图代码,并且在调用该代码时,结果由 Core Animation
缓存并在以后尽可能多地重用。重用已经渲染的内容消除了更新视图通常所需的昂贵绘制周期。在可操作现有内容的动画过程中,此内容的重用尤其重要。这种重用比创建新内容高效得多。
UIView 的属性和方法
frame、bounds、center
frame:
1、表示当前视图在父视图坐标系中的位置和大小,以父控件的左上角为坐标原点。
2、view 有 transform,则 frame 不能反映其在父view中的实际位置,需要用 bounds+center 来反映。bounds:
1、 描述视图在自身坐标系中的位置和大小,主要在绘制过程中使用。
2、bounds.origin代表自己坐标系的原点,为其 subViews 提供的坐标系。x < 0 向右,y < 0 向下。
3、bounds.size 默认和 frame.size 相等,修改它会相对于其中心点扩大或缩小视图,同时frame属性的size会随之匹配。center
表示当前视图中心点在其父视图中的位置
1 | let pyView = UIView(frame: CGRect(x: 100, y: 200, width: 200, height: 200)) |
UIKit中的默认坐标系的原点在左上角;Core Graphics
和 OpenGL ES
使用的坐标系的原点位于视图或窗口的左下角。
transform(仿射变换)
仿射变换(Affine Transform
)是一种数学矩阵,它指定了一个坐标系中的点如何映射到另一个坐标系中的点。.
UIView
的 transform
属性用于在其父视图坐标系内进行移动、缩放、旋转,这些变换操作是相对于UIView
的 锚点发生的。若要更改锚点,需修改视图底层的CALayer
的anchorPoint
属性。
1 | var transform: CGAffineTransform { get set } |
- 在ios8.0及以后版本中,
transform
属性不会影响自动布局。 - 对
UIView
进行多次transform
,转换的顺序会影响最终结果。
alpha、isHidden、isOpaque
alpha
和 isHidden
直接改变视图的不透明度。
如果你使用的UIView
是用完全不透明的内容完全填充其边界矩形,则可设置isOpaque = true
来消除不必要的合成操作来提高性能。
isOpaque = true
会告诉UIKit
它不需要在你的视图后面渲染任何内容,减少渲染可以提高绘图代码的性能。
Autoresizing(自动伸缩属性)
如下这些属性会影响View
及其subView
的自动调整大小行为:
1 | // 控制视图如何响应其父视图边界的更改 |
当对 view
采用自动布局时,需要设置view.translatesAutoresizingMaskIntoConstraints = false
。
contentMode 内容模式
当 UIView
第一次被显示时,它像往常一样呈现它的内容,结果被捕获在一个底层位图中。之后,对视图几何形状的更改并不总是会导致重新创建位图。相反,contentMode属性中的值决定了位图如何缩以适应新的边界。
当设置 view.contentMode = .redraw
时,会重新调用 draw(_:)
。改变 frame
、bounds
、contentMode
等属性,并不会调用draw(_:)
contentStretch伸缩拉伸
可以将视图的一部分指定为可拉伸的,当视图的大小更改时,仅可拉伸部分中的内容会受到影响。通常应用于UIButton、UIImage上。
只有当 contentMode
的值为:scaleToFill
、.scaleAspectFit
、.scaleAspectFill
时,才支持可拉伸视图。
1 | // 在iOS7.0中被废弃,swift中无法使用。 |
1、拉伸图片
1 | let imgView = UIImageView(frame: CGRect(x: 100, y: 100, width: 200, height: 160)) |
1 | let halfW = (img?.size.width ?? 0.0) / 2, halfH = (img?.size.height ?? 0) / 2 |
2、平铺图片
1 | let imgView = UIImageView(frame: CGRect(x: 100, y: 300, width: 300, height: 25)) |
1 | let tillImg = img?.resizableImage(withCapInsets: UIEdgeInsets.zero, resizingMode: .tile) |
contentScaleFactor
1 | var contentScaleFactor: CGFloat { get set } |
contentScaleFactor
表示视图的比例因子,决定如何将视图中的内容从逻辑坐标空间(以点为单位)映射到设备坐标空间(以像素为单位)。这个值通常是1.0或2.0。
此属性的默认值是与当前显示视图的屏幕相关联的比例因子。
通常,您不需要修改此属性中的值。但是,如果你的应用程序使用OpenGL ES
绘图,你可能想要改变比例因子以牺牲图像质量来换取渲染性能。
isExclusiveTouch(处理事件)
1 | var isExclusiveTouch: Bool { get set } |
isExclusiveTouch
表示接收器是否独占方式处理触摸事件,默认值为false
。
如果其值为 true
,则会导致接收器阻止将触摸事件传递到同一窗口中的其他视图。
添加子视图的方法
1 | // 交换2个索引子视图的位置 |
1 | // 把 yellowView 放到 view 的子视图最顶部显示 |
添加子视图的变化
1 | func willMove(toSuperview newSuperview: UIView?) |
坐标转换
1 | // 将当前视图的本地坐标系的坐标转(方法调用者)换为指定视图(传入的View参数)的坐标系统 |
drawRect、layerClass
draw(_ rect:)
属于 UIViewRendering
协议中的方法。虽然该方法是UIView的,事实上是底层 的CALayer安排了重绘工作和保存了因此产生的图片。Apple不允许直接调用该方法,当需要更新绘制时,需要调用 setNeedsDisplay()
可以在UIView的子类中重写该方法,通过 Core Graphics
技术绘制视图内容,实现其绘制代码。该方法负责将视图的内容绘制到当前的图形上下文中,该上下文由系统在调用此方法之前自动设置。我们可以获得图形上下文的引用,但不要建立对图形上下文的强引用,因为图形上下文会更改。
如果使用 OpenGL ES
进行绘制,则应使用 GLKView
类而不是子类UIView。
注意自定义 UIView 时如果不进行绘制,不要在子类中写一个空 的-drawRect:方法,这会造成CPU资源和内存的浪费。
1 | func draw(_ rect: CGRect) |
重写layerClass
方法,为UIView
关联不同类型的CALayer
。由于不同的CALayer
类型有专门的功能,选择不同的层类可能使您能够以简单的方式提高性能或支持特定类型的内容。具体可查看:Different Layer Classes
1 | override class var layerClass: AnyClass { |
布局方法
1 | func setNeedsLayout() |
其它属性方法
默认值为 false,当为true时,会对超出其显示范围的子视图的内容进行裁剪。
1 | var clipsToBounds: Bool |
是否为 view 的子视图
1 | func isDescendant(of view: UIView) -> Bool |
UIView的动画属性
frame
bounds
center
transform:在2D空间中相对于其中心点缩放,旋转或平移视图
alpha:更改视图的透明度
backgroundColor
contentStretch:更改视图内容的拉伸方式。
有效使用视图的技巧
最小化为
UIView
自定义绘图drawRect:
,只有当现有的系统视图类不能提供所需的外观或功能时。充分使用
contentMode
内容模式,来缩放视图的现有内容以适应视图的框架矩形。尽可能将视图的不透明属性设置为
isOpaque = true
来提高性能在滚动时调整视图的绘制行为
滚动可以在短时间内引发大量视图更新。如果没有适当地调整视图的绘制代码,视图的滚动性能可能会很缓慢。不要通过嵌入子视图自定义
UIControl
尽管从技术上可以将子视图添加到继承自UIControl的标准系统控件对象,但决不能以这种方式自定义它们。例如:通过在UIButton
中嵌入自定义图像视图或标签来规避这些方法,可能会导致应用程序现在或将来某个时候(如果按钮的实现发生更改)行为不正确。