下面探索Xcode的nib编辑器界面。第6章直接通过Single View Application模板创建了一个简单的iPhone项目Empty Window;它包含了一个故事板文件,我们就来使用它。在Xcode中,打开Empty Window项目,在项目导航器中找到Main.storyboard,单击进行编辑。
图7-1显示了选中Main.storyboard后的项目窗口(我做了一些调整,使得屏幕截图适合于图书的页面大小)。界面可以划分为4部分:
图7-1:编辑nib文件
1.编辑器的大部分是画布,这是你设计应用界面的地方。画布描绘了应用界面中的视图以及包含视图的部分。视图是个界面对象,它将自身绘制为一个矩形区域。“包含视图的部分”指的是我用于包含视图控制器的方式,虽然它们并不在应用界面中绘制,但也会展现在画布中;视图控制器并不是视图,但它有一个视图(并且可以控制它)。
2.编辑器左侧是文档大纲,它根据名字以层次化方式列出了故事板的内容。可以通过拖曳右边缘或单击画布左下角的按钮将其隐藏。
3.辅助窗格中的查看器是编辑当前所选对象详细信息的地方。
4.辅助窗格中的库,特别是对象库用于将界面对象添加到nib中。
7.1.1 文档大纲
文档大纲以层次化方式描述了nib中对象间的关系。根据编辑的是.storyboard文件还是.xib文件,其结构会有些许的不同。
在故事板文件中,主要部分是场景。大概来说,场景指的是一个视图控制器,外加上一些辅助材料;每个场景顶层都会有一个视图控制器。
视图控制器并不是界面对象,不过它管理着一个界面对象,我们称这个界面对象为其视图(或主视图)。nib中视图控制器的主视图未必位于与之相同的nib中,不过通常都在一个nib中;这样,在nib编辑器中,视图通常都位于画布中的视图控制器内。这样在图7-1中,画布中大大的高亮矩形就是视图控制器的主视图,它实际上位于视图控制器中。
可以在文档大纲中查看并选取视图控制器。在场景停靠栏中,它显示为一个图标;如果选择了场景中的任何东西,那么场景停靠栏就会出现在画布中视图控制器的上方(如图7-2所示)。故事板文件中的每个视图控制器都构成了一个场景。这个场景在文档大纲中表示为一种层次化的名字集合。文档大纲的顶部就是场景本身。每个场景的顶部基本上是与视图控制器场景停靠栏中相同的对象:视图控制器本身以及两个代理对象First Responder标记与Exit标记。这些对象(以图标形式显示在场景停靠栏中的对象,以及位于文档大纲中场景顶层的对象)都是场景的顶层对象。
图7-2:在故事板中选择一个视图控制器
显示在文档大纲中的对象可以分为两类:
nib对象
视图控制器、主视图与我们想要放到该视图中的任何子视图是实际的对象,这些都是潜在的对象,当nib被运行着的应用加载时,它们会转换为实际的实例。这种从nib实例化的真实对象也叫作nib对象。
代理对象
加载nib时会实例化一些实例,不过代理对象(这里就是First Responder与Exit标记)却并不表示这些实例。相反,它们表示的是其他对象,并且有助于nib对象与其他对象之间的通信(本章后面将会介绍相关示例)。你不能创建或删除代理对象;nib编辑器会自动将其显示出来。
(文档大纲中还会显示故事板入口点。它并非任何类型的对象;只是表示该视图控制器是故事板的初始视图控制器(在其属性查看器中,Is Initial View Controller选项会被勾选上),并且对应于画布中该视图控制器左侧的向右箭头。)
故事板文档大纲中所列出的大多数nib对象都会按照层次依赖于场景的视图控制器。比如,在图7-2中,视图控制器有一个主视图;该视图会以层次方式依赖于视图控制器。这是有意义的,因为该视图属于这个视图控制器。此外,拖曳到画布主视图中的任何其他界面对象都会列在文档大纲中,并且以层次方式依赖于视图。这也是有意义的。一个视图可以包含其他视图(其子视图),并且还可以被其他视图所包含(其父视图)。一个视图可以包含多个子视图,这些子视图本身又会包含子视图。不过每个视图都只有一个直接父视图。这样就会形成一个子视图的层次树,其父视图会包含这棵树,同时顶层会有唯一一个对象。文档大纲将这棵树表示为大纲的形式,这正是其名字的由来。
.xib文件中是没有场景的。如果.storyboard文件中场景的顶层对象成为.xib文件中nib的顶层对象会出现什么情况呢?不要求这些顶层对象一定要是视图控制器;它可以是,不过大多数时候,.xib文件的顶层界面对象都是个视图。这个视图可以作为视图控制器的主视图,不过这并非强制的。图7-3展示了一个.xib文件的结构,它等价于图7-2的单个场景。
图7-3中的文档大纲列出了3个顶层对象。其中两个是代理对象,它们在文档大纲中起到占位符的作用:File/'s Owner与First Responder。第3个对象是实际的对象,它是个视图;应用运行加载nib时会将其实例化。.xib文件中的文档大纲不能完全隐藏起来;相反,它会收起为一组图标,表示nib的顶层对象,类似于故事板文件中的场景停靠栏,通常也叫作停靠栏(如图7-4所示)。
图7-3:包含了一个视图的.xib文件
现在,文档大纲看起来似乎有些多余,因为其层次结构非常少;图7-2与图7-3中的所有对象都可以通过画布来访问。不过,如果故事板包含了多个场景,一个视图包含了多层对象(及其自动布局约束),这时文档大纲就很有用了,你可以通过它以非常直观的层次化结构来查看nib的内容,并且能够找到和选择所需的对象。此外,还可以在这里重新整理层次结构;比如,如果错误地将某个对象作为了一个视图的子视图,那么你可以拖曳其名字在大纲中对其进行重新定位。
图7-4:.xib文件中的停靠栏
如果文档大纲中的nib对象名是泛泛的名字且没有给出什么有价值的信息,那么你可以修改它们。从技术上来说,名字是个标签,没什么特殊含义,可以随意为nib对象分配适合的标签。在文档大纲中选择一个nib对象的标签,按下回车键使之可以编辑;或选中该对象,然后在身份查看器的Document部分编辑Label域。
7.1.2 画布
画布能以图形化的方式表示出顶层的接口nib对象及其子视图,类似于你经常使用的绘图程序。画布是可以滚动的,并且能够自动容纳其所包含的多个图形化元素;故事板画布还可以缩放大小(选择Editor→Canvas→Zoom或使用上下文菜单)。
(在.xib文件中,可以在不删除对象的情况下删除顶层nib对象的画布表示,方式是单击左上角的X,如图7-3所示。还可以在文档大纲中单击nib对象来恢复画布的图形化表示。)
这个简单的Empty Window项目的Main.storyboard只包含一个场景,因此它在画布中只会以图形化方式表示一个顶层的nib对象,即场景的视图控制器。视图控制器中是其主视图,通常无法将其与画布中的表示区分开来。当应用运行时,该视图控制器会成为应用窗口的根视图控制器;因此,其视图会占据整个窗口,实际上会成为应用的初始界面(参见第6章)。可以在这里做一些尝试:我们在该视图中所做的任何修改都会在随后构建并运行应用后显现出来。为了证明这一点,下面添加一个子视图:
1.从图7-1所示的nib编辑器开始。
2.打开对象库(Command-Option-Control-3)。如果显示为图标视图(没有文本的图标网格),请单击过滤栏左侧的按钮将其显示为列表视图。单击过滤栏(或选择Edit→Filter→Filter in Library,Command-Option-L)并输入“button”,这样列表中只会显示出按钮对象。Button对象会列在最上面。
3.将Button对象从对象库拖曳到画布中视图控制器的主视图上(如图7-5所示),松开鼠标。
图7-5:将一个按钮拖曳到视图中
现在按钮会出现在画布中的窗口上。我们所执行的动作(从对象库拖曳到画布上)是非常典型的一个动作;在设计界面时经常会这么做。
就像绘图程序一样,nib编辑器可以帮助你设计界面。下面是一些典型使用场景:
·选中按钮:修改大小处理器会出现(如果不小心选中了两次,修改大小处理器就会消失,请再次选中视图,然后选中按钮)。
·使用修改大小处理器,让按钮变得宽一些:尺寸信息会出现。
·将按钮拖曳到视图边缘:会出现一个指示,展示出标准的间隔。与之类似,将按钮拖曳到视图中心附近,当按钮居中时,指示会告诉你。
·选中按钮后,按下Option键并将鼠标悬浮在按钮外面:箭头与数字会出现,展示出按钮与视图边缘之间的距离。(如果在按下Option键时不小心单击或拖曳了,你就会看到两个按钮。这是因为按住Option并拖曳对象会将对象复制出来。选中不想要的按钮,按下Delete键将其删除。)
·按住Control与Shift键并单击按钮:会出现一个菜单,可以通过它选择按钮或按钮下面的对象(在该示例中就是视图与视图控制器,因为视图控制器是一切的顶层背景)。
·双击按钮标题。标题就变成可编辑的了。指定好一个新标题,如“Howdy!”。按下回车键来设置新的标题。
现在来验证刚才的设计,我们来运行应用:
1.将按钮拖曳到画布左上角附近(如果不这么做,那么当应用运行时按钮可能就会脱离屏幕)。
2.查看Debug→Activate/Deactivate Breakpoints菜单项。如果显示的是Deactivate Breakpoints,那就请选择它;我们不希望应用运行时在之前章节所创建的断点处暂停下来。
3.确保方案弹出菜单中的目标是iPhone 6。
4.选择Product→Run(或单击工具栏上的Run按钮)。
暂停一段时间后,iOS模拟器会打开,这个窗口不再是空的了(如图7-6所示);它包含了一个按钮!你可以使用鼠标单击该按钮,模拟用户手指的操作;在单击时按钮会高亮显示。
图7-6:Empty Window应用的窗口不再是空白的了
7.1.3 查看器与库
有4个查看器会与nib编辑器一同出现,并且会作用于在文档大纲、停靠栏或画布中所选择的对象上:
身份查看器(Command-Option-3)
该查看器的第一部分Custom Class是最为重要的。可以在这里查看并修改所选对象的类。有时需要修改nib中对象所属的类,本章后面将会对此进行介绍。
属性查看器(Command-Option-4)
这里的设置对应于代码中用于配置对象的属性与方法。比如,在属性查看器中选中视图,然后从后面的弹出菜单中进行选择相当于在代码中设置视图的backgroundColor属性。与之类似,选中按钮并在Title域中输入相当于调用按钮的setTitle:forState:方法。
属性查看器分为几部分,对应于所选对象的类层次结构。比如,UIButton的属性查看器有3部分:除了Button部分,还有一个Control部分(因为UIButton也是个UIControl)和一个View部分(因为UIControl也是个UIView)。
尺寸查看器(Command-Option-5)
X、Y、Width与Height域确定了对象在其父视图中的位置与大小,对应于代码中其frame属性;可以通过拖曳和缩放的方式在画布中完成这些操作,但这么做无法满足数字精度。
如果开启了自动布局(对于新的.storyboard与.xib文件,这是默认情况),那么尺寸查看器的其他部分就与所选对象的自动布局约束相关;此外,画布右下角的按钮可以自动管理对齐、定位与约束。
连接查看器(Command-Option-6)
本章后面将会介绍连接查看器的使用。
在编辑nib时有两个非常重要的库:
对象库(Control-Option-Command-3)
这个库是想要添加到nib中的对象来源。
媒体库(Control-Option-Command-4)
该库会列出项目中的媒体,比如,想要拖曳到UIImageView或直接拖曳到界面中的图片(在这种情况下会创建一个UIImageView)。
刚才多次提到自动布局与约束,不过这里还不打算对其进行介绍,也不会介绍尺寸等级和条件约束(画布底部的“Any”按钮)。这些都是涵盖范围广泛的主题,与视图和视图控制器紧密相关,这已经超出了本书的讨论范围。我在另一本书《Programming iOS 9》中对其进行了详尽的介绍,包括如何在nib编辑器中处理约束与尺寸等级。