2.2 了解故事板
俗话说:“兵马未动粮草先行”,在iOS应用程序开发中的“粮草”其实就是故事板(Story Board),而要想编辑故事板中的对象,则需要使用用户界面构建器——Interface Builder(简称IB)。
以前为智能手机开发应用程序的时候,程序员总是依靠纸和笔去设计流程。后来出现了流程图软件,利用这种软件就可以通过数字手段来记录工作的流程和进程。幸运的是,Xcode为iOS大咖提供了一个叫做“Story Board”(故事板)的工具来体现应用程序的工作流程。
故事板可以帮助我们创建应用程序的用户界面,但它并不仅仅是服务于一个图形界面的绘制和搭建工具。使用IB,我们可以不用编写程序代码就能轻松构建和实现一些功能(例如视图场景间的转换、控件对象的属性设置等),有效减少Bug的出现,缩短开发周期,便于后期项目的升级和维护。
Xcode中的IB有四项基本功能:
·通过IB设计应用程序的用户界面。用户界面包括窗口(window)和视图(view)以及相关的控件,例如按钮(button)、滑块(slider)、文本框(text field)等对象。还可以调整这些对象的预设属性,比如,当我们添加按钮时,可以设置其在视图中的位置、颜色以及按钮的标签等。另外,通过继承还可以自定义它的外观和其他的特性。
·使用IB可以实例化应用程序中的对象。任何被添加到nib文件或故事板文件中的对象,在载入文件的时候,都会在内存中自动创建。
·使用IB可以将代码和用户界面对象关联起来。当用户在屏幕上与视图或控件进行交互的时候,这些事件会通过IBAction触发代码文件中的方法。如果要在代码中修改用户界面对象的属性(如大小、位置、颜色或状态),就需要使用IBOutlet。它可以将Swift中的实例变量(实际上就是指向到故事板中视图对象的指针变量)与相应的用户界面中的对象建立关联。
·如果要为用户界面对象添加额外代码,就需要子类化该对象,而且还要告诉IB使用子类化的对象来代替之前的默认对象。否则,你添加的代码将不起任何作用。
接下来,我们先要创建Calculator项目。
1)在菜单中选择“File→New→Project”,然后在弹出的项目模板选择面板中选择“iOS→Application→Single View Application”。
2)将Product Name设置为Calculator,将Organization Name设置为你的名字,将Organization Identifier设置为cn.project,将语言设置为Swift,将Devices设置为iPhone,不勾选Use Core Data。在确定好本地存储位置以后,点击“Create”按钮完成项目的创建。
2.2.1 创建用户界面工具Interface Builder
在Xcode中我们可以通过编写代码来创建用户界面——先实例化一个界面对象,然后设置对象的属性,最后将它添加到视图体系之中。比如,第1章我们在ViewController类的viewDidLoad()方法中创建一个UILabel类型的对象,再将它的文字信息显示在屏幕上面。
override func viewDidLoad() { super.viewDidLoad() self.view.backgroundColor = UIColor.yellowColor() let label = UILabel(frame: CGRect(x: 10, y: 170, width: 300, height: 50)) label.text = "欢迎来到iPhone应用程序开发的世界!" label.textColor = UIColor.redColor() self.view.addSubview(label)
上面5行加粗的代码只是在视图中显示一个标签对象,并没有设置标签的字体、字号等属性。如果要在视图中同时呈现文本、按钮、图像和其他可视化控件,则需要编写更多的代码,这样会导致程序员在这些技术含量很低的代码上花费大量的精力,而且出现Bug的几率也会大大增加。
面对这样一个容易让程序员“晕圈”的情况,该是Interface Builder(简称IB)大显身手的时候了。我们可以通过IB在视图上添加各种可视化元素。但是,在IB中创建的这些可视化控件还要与项目的源代码之间建立一种简单的连接,称为关联。通过关联,就可以在代码中控制这些控件,包括控制它们所显示的文本内容、图片和状态等。如果用户在应用的运行过程中与控件有交互操作,则通过关联还会向目标类发送消息,进而执行相关的动作(方法)。
2.2.2 故事板
故事板是苹果从Xcode 4开始引入的全新界面设计和组织管理工具。故事板简单来说就是一些界面文件的集合,其中还包括视图的元数据及它们之间的相互关系。故事板将应用程序的视图(View)从数据模型(Model)和控制器(Controller)中分离出来,从而形成了Model-View-Controller(MVC)设计模式。在一个故事板中,我们可以创建多个场景(Scene),并且通过简单的拖曳操作就可以完成场景之间的切换(当由一个场景过渡到另一个场景时,需要在它们之间建立连接)。使用故事板的好处之一就是在做上面这些事情的时候不用编写任何代码。
故事板包含两个主要的内容:场景(Scene)和过渡(Segue)。
故事板不仅仅具有很酷的外观,还能创建自定义的界面对象。需要注意的是,故事板中的这些可视化对象在载入系统的时候会被自动创建和初始化。
1.场景
场景就是显示在iOS设备上的全屏视图,一般来说,每个场景都会对应一个视图控制器。场景中可以包含按钮、标签、表格视图、图像、滑块、开关等界面元素,以及一些其他的非界面元素。
2.过渡
过渡是将两个场景进行连接,允许程序从一个场景切换到另一个场景。当过渡发生的时候,在第一场景上面会呈现出第二场景。反过来说就是,第二场景被呈现在第一场景上面。
我们可以创建下面几种类型的过渡。
·push segue(推送过渡):推送过渡需要一个导航控制器或标签控制器才可以操作。当push segue发生时,在导航控制器或标签控制器的堆栈中会推送一个新视图控制器。只有这样做,导航控制器或标签控制器的堆栈才可以跟踪进栈的所有视图控制器,在需要的时候切换到指定的控制器。当我们正在使用导航控制器或标签控制器的时候就应该使用这种类型的过渡。在iPhone的通讯录应用中,当用户点击某位联系人的时候,该联系人的详细信息视图就会被push出来。
·modal segue(模态过渡):模态过渡不需要导航控制器就可以完成视图控制器之间的过渡,但是它并不具备推送过渡的导航功能,因为它只是简单地在一个视图控制器上面呈现另一个视图控制器。
·Pop-over segue(弹出过渡):弹出过渡与模态过渡类似,不同之处在于它会创建一个小窗口并呈现在当前场景的顶部。弹出过渡仅限于iPad应用程序使用。
·custom segue(自定义过渡):自定义过渡是通过代码方式来自定义两个场景间的过渡效果。
iOS中的过渡类是UIStoryboardSegue,它包含3个属性:sourceViewController、destinationViewController和identifier。其中identifier是String类型,用于标识某个特定的过渡,以方便我们在程序代码中使用。
一般来说,应用程序开始一个过渡是基于用户的交互,比如点击按钮或表格中的某个单元格,抑或是某种特定的手势。在故事板中使用一条连接两个场景的线来代表过渡,如图2-2所示。
3.故事板的文档大纲
在Calculator的项目导航中选择Main.storyboard,此时会自动打开Interface Builder。文件的内容显示在IB编辑器中,而故事板大纲则出现在IB编辑器的左侧,如图2-3所示。
图2-2 一个过渡连接两个场景
提示 如果在故事板界面中没有看到文档大纲,可以在菜单中选择“Editor→Show Document Outline”,或者点击编辑区域左下角的图标将其打开。
图2-3 Interface Builder编辑界面
当前的故事板中只有一个场景View Controller Scene,它的样子“胖胖的”,既不像是iPhone的界面,也不像是iPad的界面,我们怎么来使用它呢?这要从iOS的历史讲起。
2007年苹果发布了iPhone,并伴随第一代移动操作系统iPhone OS 1.0。当时的1.0系统功能非常简单(相对的),而且不支持第三方开发商为其开发应用程序。在iPhone OS 2.0的时候系统开始支持第三方为其开发应用,并且用户还可以从App Store上面购买自己喜欢的应用。那个时候使用Xcode开发应用只要支持iPhone的320×480像素的界面就好。
在2010年的时候,苹果推出了iPad产品,苹果不想再重新定义一个iPad OS系统(iPhone和iPad的系统基本相同)了,所以就将这两个系统整合重新命名为iOS。这样,在Xcode中我们可以为一个应用项目开发两套不同的用户界面,一个用于iPhone,另一个用于iPad。也就是说,我们可以开发一个应用程序并同时支持iPhone和iPad设备。此时,如果我们不考虑iPhone 3GS及以前产品,需要为其制作两套完全不同的界面(640×960和1024×768像素)。假如这个应用有10个场景,就要分别制作20个场景。
好吧,如果这样的一个工作量作为一个开发团队还可以接受,那么到了2012年苹果推出iPhone 5的时候,要开发一个同时支持iPhone和iPad应用,就需要为iPhone设计640×960和640×1136像素的界面,为iPad设计1024×768和2048×1536像素的界面。如果应用还是10个场景,那么一共要设计40个场景。
如果这样的工作量你的团队还可以接受,2014年9月苹果推出4.7英寸的iPhone 6,听到这里你是否要崩溃呢?将一个好的创意转化为应用已经很难了,还要在搭建界面上花那么大的力气,这是让程序员寻死的节奏呀!
因此,苹果首先在Xcode 5中引入了自动布局特性,这样可以将可视化对象在界面中的绝对位置变成在界面中的对象与对象之间的相对关系。它的好处就是不管苹果将来再开发出何种分辨率的新产品,只要为iPhone设计一套界面,再为iPad设计一套界面即可。在Xcode 6中苹果又引入了Size Classes特性,通过它程序员就能够只设计一个通用的界面,从而同时支持所有的iPhone和iPad设备。
之前我们在IB中看到的那个“胖胖”的界面,就是为了能够同时支持iPhone和iPad而准备的。因为Calculator项目只是一个运行在iPhone设备上的应用,所以接下来我们先修改场景的界面属性。
在项目导航中选择Main.storyboard,再选中故事板中的View Controller场景,使用Option+Command+1快捷键切换到文件检视窗。在文件检视窗的Interface Builder Document部分中取消Use Size Classes的勾选项,在Disable Size Classes确认面板中将Keep size class data for设置为iPhone,如图2-4所示,点击“Disable Size Classes”按钮。
因为在创建项目的时候我们选择了iPhone设备,所以在完成上面的操作以后,View Controller场景的视图马上变成了iPhone尺寸大小。
图2-4 在文件检视窗中取消Use Size Classes选项
如果想要放大或缩小故事板中的视图,可以在编辑区中通过双击鼠标来实现。或者在故事板中点击鼠标右键,通过快捷菜单来缩放视图。除此以外,也可以通过苹果的触控板的掐捏操作来实现缩放效果。
在文档大纲的View Controller Scene中主要包含3个图标:First Responder、View Controller和View。其中前两个比较特别,它们并不属于视图对象。
·First Responder:它就是一个指针变量,指向当前正在进行人机交互的界面对象。当用户打开iOS应用程序以后,可能会有多个对象负责响应屏幕触摸或键盘输入的操作。用户当前和哪个对象进行交互,First Responder就会指向谁。比如用户正在向一个文本框(UITextField类型的对象)中输入内容,First Responder就会指向到它,直到用户将焦点转移到其他界面对象。
·View Controller:它是一个视图控制器对象,在应用程序运行的过程中,视图控制器对象会载入故事板中相应的场景。它可以控制场景中的所有对象(需要和这些对象建立关联),并接受用户的交互操作。
·View:它是一个视图对象,也是一个UIView类型的对象,它就像一个容器用于呈现各种界面对象。视图控制器会载入这个视图并将其显示在iPhone的屏幕上面。视图实际上是分层的,这意味着我们可以向视图中添加各种界面控件,也可以添加子视图。除此以外,我们还可以设置界面对象的属性。
当我们构建用户界面的时候,随着界面对象数量逐渐增多,文档大纲中各个场景的View中的对象也会随之增加。有些场景可能会包含十几甚至几十个不同的界面对象,形成一个复杂的场景,如图2-5所示。
图2-5 故事板中的场景及场景中呈现的界面元素
在文档大纲中我们可以收缩或展开场景中的视图结构,以便更好地关注当下最重要的内容。
说明 这部分所介绍的视图对象(UIView对象)是一个能够包含其他视图、界面元素或响应用户交互事件的矩形区域。所有的界面元素(如按钮、文本框等)都可以添加到视图对象之中,实际上它们都是UIView的子类,同时也是大纲场景中View对象的子视图。
4.文档大纲区域中的对象
文档大纲包含了当前场景中所有可视化元素的引用,这样我们不仅可以清楚地了解到场景中所有视图的层次结构,还可以快速与程序代码建立关联。
另外,对于那些在视图中的非可视化对象(比如First Responder和View Controller),我们还可以在编辑器中场景顶部的图标栏中找到它们,如图2-6所示。
注意 在IB编辑器中当前没有被选中的场景,其顶部的图标栏中并不会显示First Responder、View Controller和Exit图标,只会出现该视图控制器的名称。如果选中该场景,则会出现上面的图标。
图2-6 故事板场景中的图标栏和文档大纲之间的关系