现在,如果你按照我们的教程,你应该有一个基本的了解UITableView和如何构建一个简单的应用程序。这个文章,我们将讨论新的东西- 故事板(Storyboard)。这是一个最令人兴奋的功能,在Xcode 4.2和iOS 5 SDK。为iOS开发者,它可以使你的生活,更简单,让您轻松地设计你的iOS应用程序的用户界面。
在本教程中,我们将向您展示如何使用故事板来建立一个导航界面,并整合与UITableView的。我们尽量使教程保持简单,重点解释概念。因此,在这篇教程里没有花哨的界面,漂亮的图形。我们将界面的设计放在以后的教程中。
好吧,让我们开始吧。
导航控制器是什么
在我们开始编码之前,像往常一样,让我们简要地介绍一下导航控制器和故事。
如下面图片展示的那样,导航控制器是另一个你常在iOS应用程序的UI元素。它可以将不同层次的内容展示在屏幕上。你可以与内置的照片应用程序或者和YouTube联系来看看。他们都使用导航控制器,以显示层次的内容。在大多数的应用程序里面,通常表视图和导航控制器一起工作。但这并不意味着你必须同时使用两者。
![照片应用程序导航控制器 照片应用程序导航控制器](http://www.appcoda.com/wp-content/uploads/2012/06/Photos-App-Navigation-Controller.jpg)
照片应用程序导航控制器的一个例子 -
故事版概述
故事板,如前面提到的,自发布的Xcode 4.2的一个新特性。它提供了一个完整的iOS开发者创建和用户界面设计的新途径。在故事板的推出之前,这是对于初学者来说,创建导航(选项卡)功能是特别困难的。每一个界面被存储在一个单独的nib文件。最重要的是,你必须编写代码来连接所有的界面,并实现了如何导航。
使用故事板,所有的界面都存储在一个文件中。这给你一个关于应用程序的可视化表示的概念性概述,并告诉您如何不同连接屏幕。Xcode提供了一个内置的编辑器演示图板布局。您可以定义各种界面,只需使用点之间的过渡(被称为segues),然后单击。这并不意味着你不需要编写代码的用户界面。但故事板明显着降低了需要编写的代码量。下面是一个示例截图,向您展示如何演示图板的样子在Xcode中。
![分镜脚本解释 分镜脚本解释](http://www.appcoda.com/wp-content/uploads/2012/06/Storyboards-Explained.jpg)
故事板 - 场景Scene和Segue
Scene(场景)和Segues
故事板工作时,场景Scene和Segues的是你经常遇到的两个名词。在故事板中,Scene(场景)是指一个单一视图控制器和视图。每个场景都有一个dock(码头?),这主要是为了产生行为与连接视图控制器和他的视图。
Segue在两个场景之间和管理两个场景之间的过渡。PUSH和Modal(模态)的两种常见过渡类型。
在故事板中创建导航控制器
现在,让我们开始动手了,去创造我们自己的故事板。在本教程中,我们将构建一个简单的应用程序,并同时使用UITableView和UINavigationController。我们使用表格视图来显示菜谱列表。当用户选择的一个菜单时,应用程序导航到下一个屏幕上显示详细信息。这将是很容易的。
首先,打开Xcode(确保你使用的是4.2或以上),并使用“Single View application(单应用程序)”模板创建一个新的项目。
![选择Xcode模板 选择Xcode模板](http://www.appcoda.com/wp-content/uploads/2012/04/Choose-Xcode-Template.jpg)
Xcode项目模板选择
点击“下一步”继续。在下面的页面中,填写所有的Xcode项目所需的内容。请确保您启用“Use Storyboards(使用故事板)”选项。
![RecipeBook Xcode项目 RecipeBook Xcode项目](http://www.appcoda.com/wp-content/uploads/2012/06/RecipeCookbook-Xcode-Project.jpg)
RecipeBook Xcode项目
点击“下一步”继续。Xcode中,然后要求你在哪里你保存你的的“SimpleTable”的项目。选择任何文件夹(例如桌面),以保存您的项目。
您可能会注意到,和以前创建的Xcode项目相比,界面有微小的差别。xib文件(界面生成器)被MainStoryboard.storyboard文件取代。
![在Xcode的默认故事板 在Xcode的默认故事板](http://www.appcoda.com/wp-content/uploads/2012/06/Empty-Storyboard-in-Xcode.jpg)
在Xcode的默认故事板
默认情况下,Xcode创建一个标准的视图控制器。如果我们将要使用导航控制器来控制屏幕的导航的话,我们首先需要改变视图控制器添加导航控制器。只要选中MainStoryboard.storyboard文件,选择“编辑”菜单中,选择“(Embed)嵌入”,其次选中“Navigation Controller(导航器)”。
![故事板嵌入在导航控制器 故事板嵌入在导航控制器](http://www.appcoda.com/wp-content/uploads/2012/06/Storyboard-Embed-in-Navigation-Controller.jpg)
嵌入在导航控制器视图控制器
Xcode中会自动嵌入RecipeBook的导航控制器视图控制器。您的屏幕应该是这样的:
![故事板添加导航控制器 故事板添加导航控制器](http://www.appcoda.com/wp-content/uploads/2012/06/Storyboard-Added-with-Navigation-Controller.jpg)
往视图控制器嵌入导航控制器
在继续之前,让我们运行的应用程序,看看它的外观。点击“运行”按钮,你应该得到一个空白视图的应用程序,但添加了一个导航栏。这说明你已经成功嵌入导航控制器。
![RecipeBookApp空 RecipeBookApp空](http://www.appcoda.com/wp-content/uploads/2012/06/RecipeBookApp-Empty.jpg)
拥有导航控制器的菜单应用程序
往表视图添加数据
接下来,我们将添加一个表视图来显示我们的食谱。在Object Library(对象库)中,选择“Table View(表视图)”,然后将其拖动到“食谱 - 视图 - 控制器”。
![故事板添加表视图 故事板添加表视图](http://www.appcoda.com/wp-content/uploads/2012/06/Storyboard-Add-Table-View.jpg)
添加表视图到 视图控制器
接下来,我们需要做的是编写代码来填充表中的数据(即菜单)。在项目浏览器中,选择“RecipeBookViewController.h”,添加TableView的一些协议,你的代码应该象下面这样:
1
2 3 4 5 |
#import <UIKit/UIKit.h>
@interface RecipeBookViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> @end |
下一步,选择“RecipeBookViewController.m”,并定义一个实例变量(即菜单数组)来存储表中的数据。
1
2 3 |
在viewDidLoad中“的方法,添加下面的代码来初始化菜单数组:
1
2 3 4 5 6 |
-
(
void
)viewDidLoad
{ [super viewDidLoad ]; // Initialize table data recipes = [ NSArray arrayWithObjects : @ "Egg Benedict", @ "Mushroom Risotto", @ "Full Breakfast", @ "Hamburger", @ "Ham and Egg Sandwich", @ "Creme Brelee", @ "White Chocolate Donut", @ "Starbucks Coffee", @ "Vegetable Curry", @ "Instant Noodle with Egg", @ "Noodle with BBQ Pork", @ "Japanese Noodle with Pork", @ "Green Tea", @ "Thai Shrimp Cake", @ "Angry Birds Cake", @ "Ham and Cheese Panini", nil ]; } |
最后,我们必须实现两个数据源的方法来填充表中的数据:“tableView:numberOfRowsInSection”和”tableView:cellForRowAtIndexPath”。回忆起这两种方法都的UITableViewDataSource协议的一部分,当配置一个UITableView时,这些都是必须实现的方法。第一种方法是用来通知一页表图中有多少行的单元格,而第二种方法是用来填充单元数据。因此,让我们添加下面的代码。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
-
(NSInteger
)tableView
:
(UITableView
*
)tableView numberOfRowsInSection
:
(NSInteger
)section
{ return [recipes count ]; } - (UITableViewCell * )tableView : (UITableView * )tableView cellForRowAtIndexPath : ( NSIndexPath * )indexPath { static NSString *simpleTableIdentifier = @ "RecipeCell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier :simpleTableIdentifier ]; if (cell == nil ) { cell = [ [UITableViewCell alloc ] initWithStyle :UITableViewCellStyleDefault reuseIdentifier :simpleTableIdentifier ]; } cell.textLabel.text = [recipes objectAtIndex :indexPath.row ]; return cell; } |
最后,我们要在表视图和我们刚刚创建的两个方法之间建立连接。回到故事板。按住键盘上的Control键,选择“表视图”,然后拖动到视图控制器图标。您的屏幕应该是这样的:
![故事板TableView中的数据源 故事板TableView中的数据源](http://www.appcoda.com/wp-content/uploads/2012/06/Storyboard-TableView-Datasource.jpg)
将表视图的数据源和委托
松开鼠标,这时会有一个弹出式的的窗口,显示“数据源”和“委托”。选择“数据源”,使表视图和它的数据源之间的建立连接。重复上述步骤,并与委托进行连接。
![故事板TableView中连接数据源 故事板TableView中连接数据源](http://www.appcoda.com/wp-content/uploads/2012/06/Storyboard-TableView-Connect-Datasource.jpg)
数据源和代理网点
在我们测试的应用程序之前,做的最后一件事是添加导航栏的标题。只需选择表视图中导航栏中的“食谱 - 视图 - 控制器(Recip Book View Controller)”,并在“(Attributes Inspector)属性检查器”填写“标题”,键入标题后敲击回车键。
![故事板添加导航栏标题 故事板添加导航栏标题](http://www.appcoda.com/wp-content/uploads/2012/06/Storyboard-Add-Navigation-Bar-Title.jpg)
指定一个导航栏的标题
现在,执行你的代码。点击“运行”按钮测试您的应用程序。如果你的代码是正确的,你应该显示显示一个列表的食谱。该应用程序和你之前已经建立的非常相似。在这里,主要的区别是,它嵌入在导航控制器。
![故事板导航栏标题 故事板导航栏标题](http://www.appcoda.com/wp-content/uploads/2012/06/Storyboard-Navigation-Bar-Title.jpg)
简单的表应用程序导航栏
引入原型单元格
你还记得我们的自定义表格单元格?几个星期前,我们向您展示如何使用Interface Builder设计自己的自定义表单元格中。简单地说,你需要创建一个分离的单元格nib文件,并在表中以编程方式加载。在故事板中,原型单元(Prototype Cell)的引进,这就变得非常简单去创建一个自定义的单元格。通过Storyboard编辑器,原型单元可以让您轻松地设计一个表格单元格中的布局。
在本次文章中,我们不会讲究自定义的细节,只是简单地添加在单元格的中的“指标”,就是那个勾啊。
要添加一个原型单元,你需要先选择表视图,在“属性”检查器“里,改变”原型单元格“的值从”0“改到”1“。当你改变值时,Xcode会自动增加原型单元格。为了展示其他表格样式,让我们将样式选项从也“Plain”改为“Group”。
![故事板原型细胞 故事板原型细胞](http://www.appcoda.com/wp-content/uploads/2012/06/Storyboard-Prototype-Cell.jpg)
表视图中添加原型单元格
接下来,选择你新加的“原型单元格”。你应该能够自定义自己的单元格了把。这里,我们将Accessory属性改为Disclosure Indicator。这里我们介绍下Identifier标识符,我们可以使用它来指定一个特殊的原型单元格。在这里,我们定义的标识符为“RecipeCell”,并与我们的代码相匹配,现在终于知道以前必须写的表示法的作用了把。
![故事板编辑原型细胞 故事板编辑原型细胞](http://www.appcoda.com/wp-content/uploads/2012/06/Storyboard-Edit-Prototype-Cell.jpg)
定义原型单元格的标识符和配件
现在,再次运行该应用程序。它看起来有点不一样了。我们正在取得进展。我们已经改变了表格样式“Group”的风格,并加入了披露指标就是箭头。
![的情节提要配方App与披露 的情节提要配方App与披露](http://www.appcoda.com/wp-content/uploads/2012/06/Storyboard-Recipe-App-with-Disclosure.jpg)
有箭头的菜单应用程序
添加详细视图控制器
最后,它涉及到本教程的最后一部分。现在缺少的是细节视图控制器来给出详细的食谱。当用户点击任何食谱时,应显示详细信息的视图控制器,。
好吧,让我们添加一个新的视图控制器作为详细信息视图控制器。
![Storyboard创建详细视图 - 控制器 Storyboard创建详细视图 - 控制器](http://www.appcoda.com/wp-content/uploads/2012/06/Storyboard-Create-Detail-View-Controller.jpg)
添加一个新的视图控制器
先前内容的主要目的是向您展示如何实现导航控制器。我们将使细节视图页面保持简单。我们只是添加一个标签来显示菜单名称。将标签从对象库拖出,并将其放置在中心位置。您可以更改字体大小或类型使标签更好看些。
接下来,我们将添加一个segue来连接的原型单元格和新的视图控制器。这是非常简单的去添加一个segue的对象。点击原型单元格,按住键盘上的Control控制键,单击鼠标左键并拉到View Controller新的视图控制器里,如下图:
![故事板添加Segue公司 故事板添加Segue公司](http://www.appcoda.com/wp-content/uploads/2012/06/Storyboard-Add-Segue.jpg)
连接这两个场景的Segue
放开两个按钮,此时将出现一个弹出窗口,上面显示了三种类型的Segues(Push(推模式)和Model(模型模式)、自定义)。
![故事板Segues 故事板Segues](http://www.appcoda.com/wp-content/uploads/2012/06/Storyboard-Segues.jpg)
故事板Segues(推,模态和自定义)
如前面所解释,Segues定义场景之间的过渡的类型。对于标准的导航,我们使用“推”。一旦选定,Xcode自动以推segue连接这两个场景。您的屏幕应该是这样的:
![故事板Segue公司的连接 故事板Segue公司的连接](http://www.appcoda.com/wp-content/uploads/2012/06/Storyboard-Segue-Connection.jpg)
故事板Segue
现在,让我们再次运行该应用程序。当您选择任何食谱,应用程序会跳转到详细视图控制器。虽然细节视图控制器只显示一个标签,你已经完成导航的工作。
![Receipe应用程序的详细控制器 Receipe应用程序的详细控制器](http://www.appcoda.com/wp-content/uploads/2012/06/Receipe-App-With-Detail-Controller.jpg)
接下来会发生什么呢?
本文是Storyboards系列教程的第二部分。在第一篇教程中,我们介绍了Storyboards是Xcode中设计用户界面的友好功能。如果你按照教程从前到后学习,你应该创建了一个简单的菜谱应用程序,并有导航界面。但是,我们没有讨论一项任务:在场景(如视图控制器)之间传递数据。
首先,我们快速回顾一下我们已经完成的工作。之前,我们学习了使用Storyboards完成的一些工作:
- 将一个正常的视图控制器嵌入到导航控制器中;
- 创建了一个表视图,并填充了菜单列表;
- 使用联线(Segue)从一个视图控制器切换到另一个视图控制器;
本文完成最后的一部分工作。在App启动的时候,显示菜单列表。轻拍其中任何一个菜单项,就切换到另外一个视图,并显示该菜单的详细信息。
我们还没有实现详细视图,该视图现在显示一个静态的标签。我们将继续完成这个项目,完善这个应用程序。
赋值视图控制器类
在第一部分教程中,我们简单创建了一个视图控制器,在Storyboard编辑器中作为一个菜单的详细视图。视图控制器默认赋值为 UIViewController 类。
回到我们的问题。视图中的标签应该随着选择的菜单发生变化。显然,在UIViewController 中需要一个变量存放菜单的名称。
事实是UIViewConroller类仅仅提供了基本的视图管理模型,相当于一个空白的视图,没有变量用于存储菜单名称。因此,不能直接使用UIViewController类,我们扩展这一类,并创建自己的类(也就是UIViewController的子类)。
在项目导航栏中,右击 RecipeBook 文件夹,选择 New File …..
选择 Cocoa Touch 栏目下的Objective-C Class作为类模板。
将类命名为RecipeDetailViewController,选择子类为 UIViewController。注意不要选择 With XIB for user interface选项,因为我们将使用Storyboards 设计用户界面,我们不必创建独立的interface builder文件。点击 Next 按钮,保存文件在项目文件夹中。
接着,我们赋值RecipeDetailViewController 类给视图控制器。返回Storyboards编辑器,选择详细视图控制器。在Identity Inspector 窗口,更改类为 RecipeDetailViewController。
添加变量到定制的类中
前面刚刚通过继承UIViewController类,创建了定制的视图控制器类。然而,这个类和父类没有任何不同,除非我们添加自己的变量和方法。还有一些工作我们需要实现的:
- 赋值一个变量(recipeName)用于数据传递 – 当用户选择Recipe视图中的一个菜单时,必须传递菜单名称到详细视图;
- 赋值变量(recipeLabel)给文本标签 – 当前标签是静态的,但应该在菜单名称变化时,更新文本标签;
OK,现在添加2个变量(recipeLabel 和 recipeName)。选择RecipeDetailViewController.h 文件,为接口添加2个属性:
@interface RecipeDetailViewController : UIViewController
@property (nonatomic, strong) IBOutlet UILabel *recipeLabel;
@property (nonatomic, strong) NSString *recipeName;
@end
选择RecipeDetailViewController.m文件,为上述变量添加 synthesis。确保代码在@implementation RecipeDetailViewController 下面:
@implementation RecipeDetailViewController
@synthesize recipeLabel;
@synthesize recipeName;
如果你忘记了接口和实现的内容,可返回访问本教程,并回顾相应的概念。
建立变量和UI 元素之间的连接
接下来,我们需要连接recipeLabel 变量到可视化的Label 标签。在Storyboards编辑器,按住Control键,并点击Recipe Detail View Controller 图标,拖拉到Label 对象上。释放按钮,弹出变量列表供你选择,选择 recipeLabel 变量。
现在,已经连接了变量和Label UI元素,变量的任何变化将可视化地显示出来。但是,仍然有一项工作没有处理。我们还需要标签显示菜单的名称。因此,在viewDidLoad 方法中,我们添加如下代码,并设置标签文本和菜单名称一致。
- (void)viewDidLoad
{
[super viewDidLoad];
// Set the Label text with the selected recipe
recipeLabel.text = recipeName;
}
尝试编译并运行你的App。哦,在选择任一菜单项后,详细视图完全显示空白。这是期望的效果,因为我们还没有编写任何代码传递菜单名称,recipeName 变量是空白的,导致文本标签也是空白的。
使用联线(Segue)传递数据
现在到了本教程的核心内容了 – 如何使用联线(Segue)在视图控制器之间传递数据。联线管理视图控制器之间的切换。在此基础之上,联线(Segue)对象用来准备视图控制器之间的切换。当联线触发时,在可视化的视图切换发生之前,storyboard运行时调用当前视图控制器的 prepareForSegue:sender: 方法(在本示例中,是RecipeBookViewController)。通过实现该方法,我们可传递任何需要的数据给即将显示的视图控制器。
然而,最好的办法是给Storyboards中每一个联线一个唯一的标识符(identifier),标识符是一个字符串,应用程序使用该字符串来区别不同的联线。随着App越来越复杂,在视图控制器中,将有更多的联线(Segue)。
选择联线,在Identity Inspector窗口,设置identifier属性值。这里,我们将该联线命名为 showRecipeDetail。
接着,我们将在Recipe Book View Controller视图控制器中实现 prepareForSegue:sender: 方法,是联线的源视图控制器。选择 RecipeBookViewController.m文件,添加如下代码:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"showRecipeDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
RecipeDetailViewController *destViewController = segue.destinationViewController;
destViewController.recipeName = [recipes objectAtIndex:indexPath.row];
}
}
在视图切换开始的时候,调用 prepareForSegue 方法。第一行用来验证segue的标识符。在本示例中,识别符为 showRecipeDetail。第二行调用 tableView:indexPathForSelectedRow 获取选择的表行。一旦获取到选择的行,我们将传递给 RecipeDetailViewController 视图控制器。一个Segue 对象包含了需要在转换结束后在视图控制器中显示的内容。你可以使用 segue.destinationViewController 获取目的视图控制器。在本范例中,目的视图控制器为 RecipeDetailViewController。剩下了的代码就是传递菜单名称给目的控制器。
你现在无法运行App。在你复制和粘贴上述方法代码到 RecipeBookViewController.m 文件中后,你应该看到一些错误信息:
上图显示了3个错误,可总结为2类:
- tableView 属性在RecipeBookViewController 中没有找到;
- RecipeDetailViewController 是什么?Xcode 不知道它是什么?
我们先讨论第二个错误。在RecipeBookViewController 中,它不知道RecipeDetailViewController。在Objective-C中,可使用 #import 指令引入其他类的头文件。通过引入 RecipeDetailViewController 的头文件,RecipeBookViewController 可以访问详细视图控制器的属性和方法。将如下代码放置在 RecipeBookViewController文件的头部纠正错误:
#import “RecipeDetailViewController.h”
关于第一个错误,你应该知道如何解决,和我们之前讨论的Label UI 元素相似。应该有相应的tableView 变量连接 UI 元素。
因此,在RecipeBookViewController.h文件中,在@end 之前添加如下代码:
@property (nonatomic, strong) IBOutlet UITableView *tableView;
在RecipeBookViewController.m文件,添加synthesis指令,告诉编译器为该tableView变量生成访问方法。
@implementation RecipeBookViewController {
NSArray *recipes;
}
@synthesize tableView; // Add this line of code
最后,返回Storyboards,连接变量到 UI 元素。在Recipe Book View Controller中,按住Control 键,点击视图控制器图片,拖拉到表视图中,释放按钮,选择 tableView 变量。
现在,所有的错误应该都解决了。现在尝试编译和运行App。这次,App应该如所期望的效果运行了。选择任一菜单项,详细视图应该显示所选择的菜单项信息。
接下来是什么?
使用导航界面创建App简单么?通过引入 Storyboard,可以显著减少需要编写的代码量。更重要的是,Storyboard界面提供了高层次地预览App的操作流程。我们希望这两篇文章让你理解Storyboard是如何工作的,已经如何使用Storyboard构建你自己的App。尽管我们创建的App比较简单,且都是一些基本的UI元素,但是它详细阐述了基本的概念,你可以基于这些知识创建更复杂的App。
在随后的教程中,我们将研究静态表单元格,并继续使用tab控制器完善App。
本文由EntLib.com Team 翻译整理,如你在创建App中遇到问题,欢迎访问 EntLib.net留言和提交你的问题。