第十章 导航控制器及场景转换
IOS 8编程入门--使用swift语言 专题目录:
...............
第七章 使用prototype Cell定制Table View(一)
第七章 使用prototype Cell定制Table View(二)
第八章 表格单元格选取以及UIAlertController(一)
导航控制器及场景转换介绍
首先,什么是导航控制器(navigation controller)?在IOS app开发过程中导航控制器是和table view一样的常用UI元素。它为分层内容提供了一个向下展开的分层接口。观察一下系统内置的照片应用,YouTube,和联系人。它们均通过导航控制器来显示层级结构。典型情况下table view控制器和导航控制器在大多数app中是配合使用的。但这并不意味着你必须同时使用。
场景及故事板中的场景转换
迄今为止我们在构建FoodPin app过程中我们只在故事板中创建了一个视图控制器。故事板允许我们做更多的事情。你可以通过点击轻易的在导航控制器中嵌入视图控制器并且创建不同场景之间的转移。在故事板中工作是场景和场景转移通常一起出现。在故事板中,一个场景指代一个视图控制器和对应的视图。每一个场景有一个连接器,它主要用来连接视图控制器和视图之间的动作和属性。场景转移主要是指两个场景之间的切换过程。Push和Modal是两种最常见的场景转移形式。
创建导航控制器
我们继续完善FoodPin app。经过我们这章改造,app仍然在表格中显示一系列餐馆,但是它会嵌入到导航控制器中。当用户选择任意一个餐馆,app将会导航到下一个界面来展示餐馆细节。
如果你已经关闭了FoodPin工程,那是时候运行Xcode并且重新打开工程了。选择Main.storyboard切换到故事板编辑器。现在你只有一个表格视图控制器(table view controller)。Xcode 提供一个嵌入功能让任意视图控制器可以很容易嵌入导航控制器。选择表格视图控制器并且点击菜单栏的编辑。选择“嵌入”(Embed in)然后选择“导航控制器(Navigation Controller)”。
Xcode 自动将表格控制器嵌入到导航控制器。我们给导航条起一个名字。在表格控制器(table view controller)中选择导航条。然后在属性查看器中设置名字为“Food Pin”。
现在运行app看看我们的app变成什么样子了。App 和之前一样只不过增加了一个导航条。
增加细节视图控制器(Detail View Controller)
很简单吧?仅仅点击了几下,你就在app中增加了导航条。现在缺少的就是另一个视图控制器用来显示餐馆细节。当用户选择任意一个餐馆,app应该转移到细节视图控制器并且显示餐馆的细节。
在界面构造器中(Interface Builder)从对象库中拖一个视图控制器出来,用于创建细节视图控制器。这章主要的目的就是教会你如何实现导航。我们尽量把细节视图设计的简单。让我们调节照片视图(Image View)来显示餐馆照片。从对象库中拖一个照片视图然后将它放在在视图的中央。设置照片的宽度为320,高度为250.
现在你在故事板中有两个视图控制器了。但是它们两之间没有关联。我们需要使用场景转换来连接它们。在音乐领域,场景转换时一段音乐和另一段音乐之间的无缝转移。在故事板中,两个场景之间的转移就叫做场景转换。
因为我们想在用户点击表格视图中的单元格时转移到细节显示场景。我们将在prototype cell和细节场景之间添加一个场景转换。按住控制键,点击prototype cell拖动到视图控制器。同时松开键盘和鼠标,将会出现一个弹出窗口显示三种场景转移类型(push, modal, custom)。
正如在前面介绍的,场景转移定义了两个场景之间的变换。在IOS中,push和modal是两种标准的转移。对于导航,我们选择“Push”。Push导航是为“逐渐向下”展示界面设计的。当我们想为某个元素提供更多信息时,典型导航选择是Push。一旦选好,Xcode自动为prototype cell 和细节视图控制器之间建立Push场景。
现在,你可以准备运行app了。一旦启动,选择任何一个餐馆然后app将会转移到细节界面。现在,细节界面控制器只显示了一个空白界面,但是好消息是你已经创建了一个导航界面了。
没有编写任何一行代码,你就在你的app中增加了一个导航控制器。很不可思议吧?我想你脑子了肯定有一系列问题吧:
1、我如何从“Restaurant Table View Controller”中把餐馆信息传递给“Detail View Controller”?
2、我如何在细节控制器中将选中的餐馆照片显示出来?
我们一个一个分析
继续前进之前的两点改进
当你将单元格和细节视图控制连接起来时,单元格上出现一个扩展指示器(disclosure indicator)灰色的V型图案暗示单元格可以点击而且当单元格被选中时将会显示一个细节视图。
在2007年之前不是这样的。在最初的iPhone发布之后7年,用户开始习惯iPhone的用户界面。对我来说并不总是需要使用扩展显示器。有时候你想关闭该显示,好让界面看起来更干净。
在界面构造器中,选择prototype cell。你可以在属性查看器中看到一个名叫“Accessory”的选项。将它的值从修改为None。
当你运行app并且选择一个单元格时app自动导航到一个空白屏幕并且显示一个动作选择列表。我们不需要动作选择列表。后面我们将在细节视图中添加一个相同功能的函数。所以从RestaurantTableViewController.swift中删除tableView(_:didSelectRowAtIndexPath:)方法。
创建一个新的类
Ok,让我们回到细节视图。在视图控制器中将更新选中的餐馆的图片。现在视图控制器默认和UIViewController 类关联。
事实上UIViewController类只提供视图关联的最基础模型。它里面没有变量用于存储餐馆图片。因此,我们扩展并且创建我们自己的类,替代UIViewController.
在项目导航栏中,右击“FoodPin”文件夹然后选择“NewFile...”。选择“Cocoa Touch Class”作为类模板。取一个类名DetailViewController并且将其设置为UIViewController的子类。点击“Next”并且在工程文件夹中保存。
正如我们知道的,我们将DetailViewController类设置为视图控制器(View Controller)。在界面构造器中,选择detail view controller。在标识查看器中(Identity Inspector)修改定制类(custom class)为DeatailViewController。
为定制类增加变量
在定制类中我们有两件事要做:
增加一个变量restaurantImage用于数据传递:当用户在表格中选择餐馆时,需要能够把照片名传递给细节视图。
为图片视图增加一个输出变量restaurantImageView:随后我们可以用选中的餐馆图片更新它。
好了,在DetailViewController.swift中添加下面的代码:
下一步,我们需要在故事板中将照片视图和restaurantImageView关联起来。右击UIImageView对象。从Reference Outlets 选项中拖动到Detail View Controller。释放鼠标按钮,选择restaurantImageView变量。
现在你已经在故事板中将照片对象和输出变量关联起来了。任何在变量上的变化都可以看到反馈。但是我们还有一件事情没有做。我们希望照片视图显示选中的餐馆照片。在viewDidLoad方法中,增加下面的代码。
当视图被加载到内存中时viewDidLoad方法被调用。你可以在这个方法里面添加对视图的定制。在上面,我们简单的将照片视图的显示照片设置为餐馆照片。
试着编译并且运行你的App。哦,细节视图在选择了餐馆之后仍然是一片空白。我们仍然遗漏了一件事情。我们没有编写任何从表格视图控制器传递餐馆图片到细节控制器的代码。因此,restaurantImage变量并没有被赋值。
使用场景转换传递数据
现在终于来到了本章最核心的部分:使用场景转换在视图控制器之间传递数据。场景转换描述视图控制器之间的转移,并且包含在转移中涉及的视图控制器。当场景转移被触发时,在可见的转移发送前,故事板运行时通过调用prepareForSegue方法通知源视图控制器(在本例中时RestaurantTableViewController)。默认的prepareForSegue方法是空方法。通过重载该方法,你可以将任何相关的书籍传递给新的控制器,在本例中时DetailViewController。
场景转移可以被多个源触发。随着你的App逐渐复杂,很可能在视图控制器之间不止一种场景转移。因此,最好的方法是在故事板中为每一个不同的场景转移提供一个不同的标识符。标识符是一串字符串,该字符串被应用用于区分不同的场景转移。
在属性查看器中选择场景转移(segue)然后可以为它取个标识符。在本例中,我们将场景转移命名为showRestaurantDetail。
场景转移配置完毕之后,在RestaurantTableViewController.swift中添加下面的代码覆盖默认的prepareForSegue方法。
代码中的第一行用于检测场景转移的标识符。这段代码只有在场景转移是showRestaurantDetail时才执行。在代码块中,我们首先再次获取被选中的行。Index path 对象返回的是一个可选类型(optional)。在swift中,你可以同时使用if 和let处理可选类型。这里的条件语句块只有在indexPath存在时才执行。
一个场景转移对象同时包含源和目的视图控制器。你可以使用segue.destinationViewController再次获取目的控制器。在这个例子中,目的控制器是DetailViewController。最后我们将餐馆名传递给目的控制器。
准备测试
现在,可以准备测试app了。点击运行按钮编译并运行app。这次,你的app应该可以按照预期的运行了。试着选择餐馆,被选中的餐馆细节将会展示出来。
你的练习
在细节展示界面中显示更多的餐馆细节是不是会更好。试着卫显示的餐馆增加名字,类型,地址等标签。如果你理解了如何传递数据,增加这些功能将不会太难。
你可以下线本章Demo 作为参考 http://pan.baidu.com/s/1i4Vu0U5