Storyboards Tutorial 02
Adding a Table View Controller
连接到tab bar controller中的两个窗口都是regular UIViewControllers.你将使用UITableViewController来替换第一个tab
选择第一个view controller然后delete它。拖一个table view controller。
选择table view controller,然后在xcode上方的菜单栏中选择Editor\Embed In\Navigation Controller。这就会添加另外一个view controller
当然你也可以从库中拖一个navigation controller,但是这个更方便。
因为navigation controller也是一个container view controller,它也有一个关系箭头指向table view controller。可以在Document Outline中查看这些关系。
注意现在table view controller有了一个navigation bar。Interface Builder自动做到。
让我们连接这两个窗口到tab bar controller。 Ctrl-drag从tab bar controller 到navigaiton controller。一个小窗口pop menu会出现。
选择Relationship Segue – view controllers,这个就在这两个窗口间创建了新的关系。
你 会发现在tab bar controller中新出现了一个tab。你可以将这个对应也navigation controller的tab和另外一个通过拖拉的方式换位置,这样第一个出现的就是navigation controller中的table view了。
启动app后
修改tab的名字-第一个tab修改为“players”,第二个修改为“gestures”。但是并不是你想象的在tab bar controller内部去修改,而是在这两个tab所关联的view controllers中去修改才会有效果。
当你连接一个view controller到tab bar controller的时候,会给他一个tab bar item的选项,你就要利用这个来修改tab的title和image
添加图片,首先请先下载图片资源,resources for this tutorial 包含一个叫做Images的文件夹。添加到工程中去。在player tab bar item中选择players.png图片,给gesture item选择gestures.png文件。
嵌 入到navigation controller中的view controller都有一个navigation item的对象来配置navigation bar。选择table view controller中的navigation item并且修改他的title为players。或者双击navigation item来修改。
运行app后效果:
Prototype cells
prototype cells允许你轻松的为你的table view设计一个自定义的layout。在Attributes inspector中设置style选项为subtitle,会即刻出现一个含有两行的cell。
在Accessory中选择Disclosure Indicator,在Identifier中设置PlayerCell.所有的prototype cells依然是regular uitableviewcell objects因此需要一个复用标示符。
运行app,发现没有任何的改变。这并不奇怪,你还需要为这个table设置data source然后它才会知道需要显示什么。
选择新建文件,选择Objective-C class模版。将类命名为PlayersViewController并且继承自UITableViewController. With XIB for user interface选项不应该勾选因为你已经设计好了view controller。
到storyboard中选择table view controller,在Identity inspector中将class设置为PlayersViewController。这个是关键的一步,将storyboard中的窗口和你自己的view controller子类连接起来!
在PlayersViewController.h:中添加mutable array property
这个数组将保存app的mian data model,包含player 对象。新建立一个Player类继承自nsobject。
在AppDelegate.m中improt 这个player和playersviewcontroller类并且添加一个新的变量_players
修改application:didFinishLaunchingWithOptions:
方法
你可以从上面的代码中注意到
Note: This is one of the limitations of storyboards. With nibs you always had a reference to the App Delegate in your MainWindow.xib and you could make connections from your top-level view controllers to outlets on the App Delegate. That is currently not possible with storyboards. You cannot make references to the app delegate from your top-level view controllers. That’s unfortunate, but you can always get those references programmatically, which is what you do here.
注 意:这个就是storyboards中其中一个限制。使用nibs文件的时候你总是可以在mainwindow.xib中一个app delegate的饮用并且你可以通过outlets来建立app delegate和view controllers的连接。但是在storyboards中不行。你不能使用引用但是依然可以通过编码实现。
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
storyboard的initial view controller是一个tab bar controller。因此你可以查找到window的rootViewController
及是这个uitabbarcontroller。
UINavigationController *navigationController = [tabBarController viewControllers][0];
playersviewcontroller在一个navigationcontroller内,而这个navigation contoller又是在第一个tab中,所以通过这个索引就可以查找到uinavigationcontroller对象。
PlayersViewController *playersViewController = [navigationController viewControllers][0];
然后查找anvigationcontroller的root view controller,就是playersviewcontroller
到此为止我们就拥有了一个有内容的player 对象群了。
打开PlayersViewController.m
#import "Player.h"
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.players count];
}
关键的地方是这个cellforrowatindexpath方法,之前的标准写法是
意思是你会询问table view去复用一个cell并且如果没有free的cells可以重用就返回nil然后你可以创建一个新的cell类的实例。但是现在不再需要这样做了!
唯一需要做的就是获取一个新的cell。如果没有可以复用的cell,会自动创建一个prototype cell的副本并返回给你。所有你需要做的就是使用在storyboard编辑器中设置的re-use identifier。可以查看前面有关的设置。
PlayerCell是之前设置的identifier。千万不要忘记设置这个identifer,否则这个复用功能不会工作的!
再次运行就可以看到效果了
Designing Your Own Prototype Cells
在Main.storyboard中选择prototype cell设置他的style属性为custom。可以使用Row Height设置cell的高度。拖两个Label对象和一个Image View,设置宽度为81,设置mode为center。
因为是自定义的,所以不再使用UITableViewCell
’s textLabel
and detailTextLabel
。给Name label tag 100, the Game label tag 101,and the Image View tag 102.
在PlayersViewController.m中添加一个新的方法imageForRating
修改tableView:cellForRowAtIndexPath
最终运行后的效果:
Using a Subclass for the Cell
虽然table view运行的不错了但是还是不太喜欢用tags来访问labels和其他的subviews。所以使用outlets会更容易操作。
首先新建立一个class叫PlayerCell,继承自UITableViewCell
PlayerCell.h中修改为:
在Main.storyboard中选择prototype cell并且将class设置为playercell。到此每次问table view一个新的cell时候dequeueReusableCellWithIdentifier
返回的就是一个playercell
的实例而不是regular uitableviewcell了。
然后将labels和image view连接到和谐outlets上去。
Important:
You should hook up the controls to the table view cell, not to the view
controller! You see, whenever your data source asks the table view for a
new cell with dequeueReusableCellWithIdentifier
, the table view doesn’t give you the actual prototype cell but a copy (or one of the previous cells is recycled if possible).
This means there will be more than one instance of PlayerCell
at any given time. If you were to connect a label from the cell to an
outlet on the view controller, then several copies of the label will try
to use the same outlet. That’s just asking for trouble. (On the other
hand, connecting the prototype cell to actions on the view controller is
perfectly fine. You would do that if you had custom buttons or other UIControls
on your cell.)
在PlayersViewController.m中
Where To Go From Here?
Check out part two of this tutorial, where we’ll cover segues, static table view cells, the Add Player screen, a game picker screen, and the downloadable example project for this tutorial!