iOS 5 故事板进阶(2)
让我们回到游戏排行窗口Ranking。创建一个 UITableViewController子类,命名为 RankingViewController。
编辑 RankingViewController.h内容如下:
@interface RankingViewController: UITableViewController
@property(nonatomic, strong) NSMutableArray*rankedPlayers;
- (IBAction)done:(id)sender;
@end
在故事板编辑器中,将 Ranking 场景的类设置为 RankingViewController。加一个Done 按钮到它的 navigationBar 并将它的 action 连接到 done action 方法。
提示:你可以简单地用右键从 Done 按钮拖到状态栏,这将以 ViewController作为连接终点。
删除 Table View 中的模板 cell。对于这个场景我们会用传统方式创建cell。传统创建单元格的方式仍然是可用的,你甚至可以将它和模板cell 一起使用。表格中的一部分 cell 使用模板 cell ,而另一部分使用传统的方式创建。(和静态cell 混合使用也是可以的,不过也需要更多的技巧)。
最终 Ranking 窗口设计效果如下:

注意:在写至本章为止,iOS 中存在一个 bug,如果向 TabBarController 所包含的 Viewcontroller 中添加手势识别器,会导致程序崩溃。因此,你可以通过按钮来触发segue。
实现 RankingViewController.m中的数据源方法:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInt
eger)section
{
return [self.rankedPlayers count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPat h:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitlereuseIdentifier:CellIdentifier];
}
Player *player= [self.rankedPlayersobjectAtIndex:indexPath.row];
cell.textLabel.text = player.name;
cell.detailTextLabel.text = player.game;
return cell;
}
当然,我们需要导入 Player 类并合成 rankedPlayers 属性。
#import"Player.h"
@implementation RankingViewController
@synthesizerankedPlayers;
最后,在done action 方法中:
- (IBAction)done:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
该 ViewController 不需要实现delegate 方法。我们并不需要向Ranking 的委托对象返回任何东西。因此在用户触摸 Done 按钮时,仅仅是将它自己解散。
运行 app。你可以打开和关闭 Ranking 窗口,虽然它只是一个空白窗口。我们需要让它显示经过分级的玩家列表。
在 GesturesViewController.h中添加属性:
@property (nonatomic, strong)NSArray *players;
在 GesturesViewController.m中合成它:
@synthesize players;
同时导入两个头文件:
#import"RankingViewController.h"
#import "Player.h"
在 prepareForSegue 方法中为两个手势配置不同的segue:
- (void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:@"BestPlayers"]) {
UINavigationController *navigationController=
segue.destinationViewController;
RankingViewController *rankingViewController =
[[navigationController];
rankingViewController.];
rankingViewController.title = @"BestPlayers";
} else if ([segue.identifier isEqualToString:@"WorstPlayers"]) {
UINavigationController *navigationController=
segue.destinationViewController;
RankingViewController *rankingViewController =
[[navigationController];
rankingViewController.];
rankingViewController.title = @"WorstPlayers";
}
}
对于两个 segue,我们首先获取位于 segue 终点的 NavigationController,进而获取RankingViewcontroller 实例并设置它的 rankedPlayers 和 title 属性。
playersWithRating 方法实现如下:
- (NSMutableArray *)playersWithRating:(int)rating
{
NSMutableArray*rankedPlayers =
[NSMutableArray arrayWithCapacity:[self.players count]];
for (Player *player in self.players) {
if (player.rating== rating)
[rankedPlayers addObject:player];
}
return rankedPlayers;
}
for循环遍历玩家列表,将指定界别的玩家添加到一个数组中。
现在的问题是,GesturesViewController 从哪里获得玩家列表?当然是从AppDelegate。
在 AppDelegate.m 中导入头文件:
#import"GesturesViewController.h"
在 didFinishLauchingWithOptions: 方法中底部加入代码:
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// ...已有的代码...
GesturesViewController *gesturesViewController= [[tabBarController ];
gesturesViewController.players= players;
return YES;
}
现在所有的模型数据已准备妥当,你可以运行程序了。向右扫,显示所有 5 星玩家,双击显示所有1 星玩家。
但工作还未完成。现在,我想把 Ranking 场景和 RatePlayer 场景连接起来。
在故事板中,用右键,从 Ranking 场景拖一个 Push segue 到Rate Player,命名为 RatePlayer。现在有两个 segue 连接到了 Rate Player 场景,名字都叫做 RatePlayer,但它们的源场景不同。
对于 RatePlayerViewController,它不关心会收到多少segue,以及 segue 的另一边(源场景)是哪个类。它仅仅是需要通过它的 player 属性接收一个 Player 对象,然后过委托对象返回调用它的 ViewController。事实上,它不知道(也不关心)调用它的segue。
如果不使用委托而使用硬编码的方式,很难从RankingViewController 或其他场景跳转到 RatePlayerViewController。问题是RatePlayerViewController 根本不知道 PlayersViewController 或者 RankingViewController。它只能向实现了RatePlayerViewControllerDelegate 的对象发送消息。委托模式使你的代码模块化、可重用,避免难以预料的后果。
由于我们没有在 Ranking 窗口中使用模板cell,我们可以用编码的方式触发segue。在 RankingViewController.m 中修改 didSelectRowAtIndexPath 方法为:
- (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath
{
Player *player= [self.rankedPlayersobjectAtIndex:indexPath.row];
[self performSegueWithIdentifier:@"RatePlayer" sender:player];
}
我们首先检索出对应的 Player 对象,然后将它放在 performSegueWithIdentifier方法中传递。这可能跟 sender 的原义有点不一致,但这种方式是完全可行的。
当然,我们也需要实现 prepareForSegue 方法:
- (void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:@"RatePlayer"])
{
RatePlayerViewController *ratePlayerViewController=
segue.destinationViewController;
ratePlayerViewController.delegate = self;
ratePlayerViewController.player = sender;
}
}
在 RankingViewController.h中,加入 import 语句并声明要实现的协议:
#import"RatePlayerViewController.h"
@interface RankingViewController: UITableViewController
<RatePlayerViewControllerDelegate>
最后在RankingViewController.m 中实现协议方法。其实仅仅是关闭界面:
#pragmamark - RatePlayerViewControllerDelegate
- (void)ratePlayerViewController: (RatePlayerViewController *)controller
didPickRatingForPlayer:(Player *)player
{
[self.navigationController popViewControllerAnimated:YES];
}
运行 app。你现在已经可以通过 Ranking 窗口为玩家打分了。
注意:可以在自加速计和陀螺仪的事件或其他故事板编辑器中不能显示的事件中用performSegueWithIdentifier 方法触发 segue,想象力是你唯一的限制(当然也别太挑战用户的底线)。
为了让程序显得更加合理,一旦玩家级别被修改,我们应该将玩家从Ranking 窗口中移除,因为很可能该玩家不再是最佳(5星)或者最差(1星)玩家了。
在 RankingViewController.h 中加入一个新属性:
@property(nonatomic, assign) int requiredRating;
罗列最佳玩家时,将该属性设置为 5,在罗列最差玩家时则设置为1。
在 RankingViewController.m 中合成该属性:
@synthesize requiredRating;
修改RatePlayerViewControllerDelegate 的协议方法:
- (void)ratePlayerViewController: (RatePlayerViewController *)controller
didPickRatingForPlayer:(Player *)player
{
if (player.rating != self.requiredRating)
{
NSUInteger index = [self.rankedPlayers indexOfObject:player];
[self.rankedPlayers removeObjectAtIndex:index];
NSIndexPath*indexPath =
[];
[self.tableView deleteRowsAtIndexPaths: [NSArrayarrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
}
[self.navigationController popViewControllerAnimated:YES];
}
一但玩家级别被修改,我们将该玩家从数组和列表中删除。
在 GesturesViewController中,我们必须设置 requiredRating 属性:
- (void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender raywenderlich.com
{
if ([segue.identifier isEqualToString:@"BestPlayers"])
{
rankingViewController.;
}
else if ([segue.identifier isEqualToString:@"WorstPlayers"])
{
rankingViewController.;
}
}
测试程序运行的效果。
提示: 我们也应该刷新 Players 窗口,这个工作留给读者自己去完成。
iOS 5 故事板进阶(2)的更多相关文章
- iOS 5 故事板进阶(1)
译自<iOS 5 by tutorials> 在上一章,你已经学习了故事板的基本用法.包括如何向故事板中添加 View Controller,通过 segues 切换 View Contr ...
- iOS 5 故事板入门(4)
原文: http://www.raywenderlich.com/5138/beginning-storyboards-in-ios-5-part-2 让 AddPlayer 窗口动起来 现在,我们先 ...
- iOS 5 故事板入门(3)
原文: http://www.raywenderlich.com/5138/beginning-storyboards-in-ios-5-part-2 Segues 介绍 是时候在我们的故事板中加入更 ...
- iOS: 使用故事板和xib设置按钮圆角方法
使用storyboard如何设置圆角或边框? 通过storyboard的 运行时属性runtime attribute,可以对Button设置圆角或者边框 1.很多人都知道,通常设置一个 Button ...
- iOS系列 基础篇 02 StoryBoard 故事板文件
iOS基础 02 StoryBoard 故事板文件 目录: 1. 故事板的导航特点 2. 故事板中的Scene和Segue 3. 本文最后 在上篇HelloWorld工程中有一个Main.storyb ...
- [IOS 开发] TableView、多个TableViewCell、自定义Cell、Cell上画画(故事板+代码方式)
第一步: //UserTableViewCell.h这里定义第一种Cell #import <UIKit/UIKit.h> @interface UserTableViewCell : U ...
- [Swift实际操作]八、实用进阶-(8)使用performSegue在故事板页面之间进行数据传递
本文将演示故事板页面之间的数据传递.首先在一个空白项目中,打开项目自带的故事板文件(Main.storyboard).故事板中已经拥有了一个视图控制器,点击选择该视图控制器.然后依此点击[Editor ...
- [Xcode 实际操作]九、实用进阶-(22)Storyboard故事板的常用布局结构
目录:[Swift]Xcode实际操作 本文将演示如在使用故事板搭建项目时,常用的一种故事板布局结构. 在项目导航区,打开故事板文件[Main.storyboard] 当前故事板中只有一个视图控制器控 ...
- [Xcode 实际操作]九、实用进阶-(23)多个Storyboard故事板中的页面跳转
目录:[Swift]Xcode实际操作 本文将演示多个Storyboard故事板中的页面跳转. 使用快捷键[Command]+[N]创建一个新的故事板文件. (在项目文件夹[DemoApp]上点击鼠标 ...
随机推荐
- spring ioc 注解配置
要注意spring 版本与jdk的兼容性 applicationContext-resource.xml: <beans xmlns="http://www.springframewo ...
- centos7 opencc 安装
繁体字转换:http://xh.5156edu.com/jtof.php 转换的有问题http://tool.lu/zhconvert/ git网址:https://github.com/BYVoid ...
- 发布Maven项目 nexus
1.在pom.xml文件中配置需要发布的工厂 如果想把项目发布到nexus中,需要在pom.xml中配置releases和snapshots版本发布的具体repository <distribu ...
- mysq 数据库基本管理
命令表: 1.设置mysql数据库密码 mysqladmin -u root password 'new-password' 'new-password'为数据库密码. 2.登陆mysql数据库 my ...
- 120. Triangle(Array; DP)
Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent n ...
- 85. Maximal Rectangle (Graph; Stack, DP)
Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing all ones and ...
- TZOJ 2754 Watering Hole(最小生成树Kruskal)
描述 Farmer John has decided to bring water to his N (1 <= N <= 300) pastures which are convenie ...
- Dreamweaver 调字体大小
编辑--首选参数--字体 这里是指调代码字体的大小,就是为了编码时看的清楚些
- struts框架中OGNL表达式的使用之jsp页面获取action中的属性值
在jsp页面中获取action中的值: 1.写一个action类OgnlAction类: 需要注意的地方: 如果在aciton中直接使用ognl表达式,将值存储的值栈中,是不能通过跳转将值传到jsp页 ...
- APP UI结构-首页功能点大集锦,很干很详细
APP UI结构的系列的文章有一段时间没有更新了,因为最近在学一些新东西和看一些新书籍,适当的给自己充电也是为了更好的输出,言归正传,今天想跟大家聊的是和首页相关的一些内容,可能有些内容最近有的小伙伴 ...