View Programming Guide For IOS 官方地址
View and Window Architecture(视图与窗口的结构)
View Architecture Fundamentals(视图的基础结构)
当你处理视图相关的问题时,最常接触到的类是UIView. View 对象在屏幕上定义了一块矩形区域.并且管理这块区域的点击事件. View 也可以作为其他 View 的父视图.并且管理对应子 View 位置和大小. UIView 类做了大量的管理各个视图的关系,你也可以自定义这些关系.
视图与 Core Animation 的 layer 结合在一起,用于管理视图动画内容相关.每个UIKIt中的视图下层都有 layer 对象 .管理下层储存和管理视图动画相关.大多数情况下,你执行相关的效果只需要使用 UIView 的公开属性就可以了.然后,如果你想得到更多的控制视图动画相关的能力,你可以使用视图的layer层.
为了更好的理解视图与layer相关,我们通过下面的例子来理解.
Core Animation 的 layer 对象的功能对底层的实现有影响.实际上绘制视图的代码尽可能少去调用.当代码被调用,绘制结果将被 Core Animation 缓存下来,并且尽可能的重用.重用已经绘制好的视图可以减少重新绘制视图所造成的额外消耗.重用机制在动画执行时有重要作用.
View Hierarchies and Subview Management
父视图通过数组来保存子视图,子视图在数组中的顺序是会影响视图的显示的
父视图中有两个子视图,这两个子视图有重叠部分,那么后添加(或者是被移动到数组最后)的视图将会显示在上面.
父视图子视图的关系也会影响视图行为,改变父视图的大小将会导致子视图的位置跟大小发生改变.其他改变也会影响子视图,比如:
1、隐藏父视图
2、改变父视图的透明度
3、改变父视图的坐标系统
视图的排布决定了程序如何响应点击事件.
当一个点击事件发生在屏幕上时,系统将发送事件到对应的视图上,对应视图来处理事件.如果对应的视图没有处理事件的能力,那么事件将传给对应视图的父视图.如果父视图也不能处理,那么事件将传给父视图的父视图.以此类推.
这是一个响应者链 Response Chain .
视图也可以将事件传给交互的实体,比如viewController.
如果没有实体处理事件,事件最终将传给应用实体 (Application Object).它来统一丢弃(discard)
The View Drawing Cycle
UIView类是以 “按需绘图模式" 去展现内容.
当一个视图第一次展现在屏幕上时,系统会要求它去绘制自己的内容.
系统将获取View中内容的 snapshot(截屏)并用截屏 snapshot(截屏)作为视图的可视部分的代替.
如果你从未改变视图内容,那么视图绘制代码将不会被执行.
对应的 snapshot(截屏)会被重复利用.在大多数跟这个视图相关的操作中.如果你改变了视图的内容,你去通知系统对应的视图发生了改变.视图将重复绘制视图的过程并且获取视图新的截屏.
当视图的内容发生改变,你不需要直接去重新绘制视图.
你通过使setNeedsDisplay和setNeedsDisplayInRect:方法主动去作废之前的视图.
这些方法告诉系统视图的内容被修改了,在下个opportunity需要重新绘制.
系统将会在下次初始化绘制动作前和现在的runloop结束后重新绘制.
这个等待将会给你充分的时间去(invalidate)作废多个View,或者是从View层级中移除视图,隐藏视图,改变视图大小,改变视图位置.
所有你做的改变将会同时反应出来.
到了绘制 view 的内容的时机,真正决定绘制内容是取决于 view 和 view 的属性.
系统视图实现了私有的绘制方法,并在头部展示出来.
一些系统的视图类将接口暴露出来,你通过修改相关属性来控制视图的显示.
自定一个视图UIView子类,你可以通过重写子类中drawRect:方法,去绘制视图.还有其他方式来提供视图内容,比如直接设置内容的(underlying layer)下层.但是drawRect:方式是最通用实现方法.
Content Modes
每个view都有 content mode, content mode 的作用是在视图的坐标发生改变时,如果去回收利用内容。当一个view第一次被展示时,它的内容将会被捕获,捕获的结果以bitmap的形式.改变坐标系时,bitmap将不会被重新绘制. content mode 属性决定bitmap该如何被拉伸。或者仅仅只是移动位置。
视图的 Content Mode 将被应用在下面的情况
- 当你改变 frame 或者 bounds 的高和宽时,将会应用 contentmode.
- 当修改 View 的 transform 属性时,transform 属性包含放大所有的因素时。
大多数view的 contentmode 是 UIViewContentModeScaleToFill
请自行查找 contentmode 各个值
StretchableViews(可拉伸区域)
你可以定义一块区域作为可拉伸的区域,当view的大小改变时,只有在可拉伸区域的内容受到影响.
经常人们会定义一个可拉伸区域,作为试图的一个重复的模块
可拉伸区域可以从xy轴两个方向拉伸
通过contentStretch属性来申明一个可拉伸的区域,值的范围是0~1
contentmodel在可拉伸区中如何拉伸中起到很重要的作用.可拉伸区域只有在contentmode导致视图内容被放大或者缩小时.
这意味着只有在contentmode为以下内容时才可以被拉伸:
- UIViewContentModeScaleToFill,
- UIViewContentModeScaleAspectFit,
- UIViewContentModeScaleAspectFill
Built-In Animation Support
在每一个视图(View)下面都有一个 layer (图层)的好处是,我们可以很容易完成跟view相关的动画.
想要执行一个动画,你必须要做一下两个步骤.
- 告诉UIKIt,将要执行一个动画
- 改变属性的值
改变以下UIView属性时,将会有动画
frame — Use this to animate position and size changes for the view.
bounds — Use this to animate changes to the size of the view.
center — Use this to animate the position of the view.
transform — Use this to rotate or scale the view.
alpha — Use this to change the transparency of the view.
background Color — Use this to change the background color of the view.
content Stretch — Use this to change how the view’s contents stretch.
Core Animation 将会告诉你如何开发动画
视图的几何意义上的坐标系
在UIkit中以左上角为坐标原点,使用 floating point number
每一个view和屏幕都有自己的坐标系。
The Relationship of the Frame,Bounds,and Center Properties
视图
默认情况下,子视图超出父视图时,父视图将不会裁剪子视图.你可以设置clipsToBounds,来改变这个效果.
不管子视图有没有超区父视图,当事件传过来时,只会在父视图的frame中响应。
Points Versus Pixels 点与像素之间的关系
Points(点)提供一个frame(矩形区域)用来绘图。
ios是一个以点为计量单位来定义了用户坐标空间的系统
一个点并不是代表一个在屏幕上的像素
The Runtime Interaction Model for Views 运行时交互
当用户点击你的交互界面,或者你代码修改一些object,一系列复杂的事件将要被触发在UIKit中,来管理交互.这些事件是有序列的。在UIKit调用这个特定序列的事件中特殊时期,UIKit将会调用你的类,来让你的类处理对应的交互事件。
下面是这个事件队列。
用户点击屏幕
硬件报告事件给UIKit
UIKit打包touch事件到UIEvent事件,并将对应事件发送到对应的视图。
-
视图的事件处理代码将被触发来回应对应的事件。
- 改变视图的属性
- 调用setNeedsLayout方法 标记view需要 layout
- 调用setNeedsDisplay: 或者 setNeedsDisplayInRect:去标记视图需要重新绘制
- 通知视图控制器去改变部分数据
-
如果视图的几何坐标修改了,UIKit需要根据下面的规则更新它的子视图。
- 如果你设置 autoresizing,那么UIKit将会根据你的设置来调整视图。
- 如果你的视图实现了layoutSubviews 方法,UIKit将会调用它。
- 你可以重写此方法来自定义视图。使用本方法来调整子视图位置和大小。举个例子,视图提供一个大的滑动区域。
如果任何一个视图被标记为重新绘制,UIKit将会要求视图去重写绘制
任何更新完之后的视图将被复合成对应区域的内容,发给绘图硬件绘制。
绘图硬件将绘制完好的内容展现到屏幕上。
总结一下如何自定义一个视图
-
事件响应方法
- -touchesBegan:withEvent:
- -touchesMoved:withEvent:
- -touchesEnded:withEvent:
- -touchesCancelled:withEvent:
layoutSubviews
drawRect
上面的方法是我们通常自定义一个视图需要重写的方法。但你不必要每个方法都去实现。如果你想要用手势来处理事件,你不需要去重写上面的所有方法。通常,如果你的视图没有包含子视图或者他自己的大小没有改变,那么就没有重写layoutSubview的必要了。最后,当你需要在运行时来修改视图的和你正在使用 UIKit 和 Core Graphics 去绘图时 你可以重写 drawRect 方法是、
window
一个应用程序至少有一个window
window的作用
1.包含可视部分
2.发送事件给你的视图和其他object
3.跟viewcontroller一起工作,加速旋转屏幕的效果
跟Window相关的任务
大多数程序,只有在初始化window时才与window发生交互.然后你可以使用程序窗口来执行一些跟程序相关的任务.
- 使用window将点和矩形的块转换到以window坐标系的坐标系统
- 使用窗口通知去响应窗口相关改变.
Creating and Configuring a Window(创建和配置window)
我们可以通过 Interface Builder 或者代码去创建Window.不管使用哪种方法,我们需要在启动的时候创建Window.并且我们需要retain window. store a reference 在我们应用程序的代理中UIApplicationDelegate实例. 如果你的程序创建了一个新的Window,使用懒加载(需要的时候再加载).
你必须在创建程序的主window,在程序启动的时候.不管是在前台foreground,还是后台background.当你的程序在后台启动的时候,你需要保证你的window不被显示,直到你的应用程序到前台工作.
Creating window in Interface Builder
xcode项目自动为你做好创建主屏幕的工作.每个项目中都包涵一个Main.storyBoard.除此之外,模板还创建一个outlet,对应程序的主屏幕.你通过outlet去获取主屏幕.
通过Interface builder创建主屏幕是,要在launch option打开 Full Screen.如果你不这样做,你的主屏幕可能比设备的屏幕小,用户动作可能不能被你的视图处理.因为主屏幕接收不到屏幕意外的touch 事件.因为view默认不会被主屏幕裁剪.View可以展现在屏幕上,但是有可能不处理touch事件.将launch option 中对应key 设置为全屏.使得window能适应整个屏幕.
如果你想更新使用Interface builder来创建主屏幕.现在Interface builder拖进一个屏幕window object到你的nib文件中.你还需要做到以下几步.
- 想要在运行时获取当前屏幕,你需要将window跟一个outlet关联起来.通常做法是在 应用代理中或者 nib 文件对应的 File owner.
- 如果你想要升级,你需要将程序的主nib文件设置为当前的文件.你还必须在 plist中设置 NSMainNibKey的值为你的nib文件名.改变这个值时,你需要确认 nib文件已经被加载并且能被获取到.
Creating a Window Programmatically 代码创建屏幕
代码创建主屏幕,你应该在程序代理的application:didFinishLaunchingWithOptions:方法中创建下面的代码
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
Displaying Content on an External Display
想要显示外置的内容,你需要给你的应用添加额外的window.通过screen object去获取,并展示额外的内容.
通常新的window可以被main Screen获取到.改变window的screen object致使你屏幕的内容发生响应的改变.只要window可以被当前screen获取到,你就可以像在主屏幕中添加view并展示一样做相同的操作.
UIScreen类拥有一个screen object的列表.代表着可以获取的物理显示.通常,只有一个screen object作为ios应用程序的主显示,但是设备是支持额外显示功能的.所有可以拥有其他的screen.有retina display显示功能的才有额外显示功能.3gs就没有这个功能.
在应用程序构建时,注册screen 连接和断开通知
-
当需要显示的时候,创建并配置一个window
- 通过UIScreen的screen属性去持有一个screen object,做一些展示额外显示的准备.
- 创建一个UIWindow实体并配置响应的大小位置.
- 设置UIScreen object为Window的screen属性.
- 适配screen object,为了支持你所以要显示的内容.
- 添加合适的view到UIWindow上.
显示这个window