让我们回到游戏排行窗口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)的更多相关文章

  1. iOS 5 故事板进阶(1)

    译自<iOS 5 by tutorials> 在上一章,你已经学习了故事板的基本用法.包括如何向故事板中添加 View Controller,通过 segues 切换 View Contr ...

  2. iOS 5 故事板入门(4)

    原文: http://www.raywenderlich.com/5138/beginning-storyboards-in-ios-5-part-2 让 AddPlayer 窗口动起来 现在,我们先 ...

  3. iOS 5 故事板入门(3)

    原文: http://www.raywenderlich.com/5138/beginning-storyboards-in-ios-5-part-2 Segues 介绍 是时候在我们的故事板中加入更 ...

  4. iOS: 使用故事板和xib设置按钮圆角方法

    使用storyboard如何设置圆角或边框? 通过storyboard的 运行时属性runtime attribute,可以对Button设置圆角或者边框 1.很多人都知道,通常设置一个 Button ...

  5. iOS系列 基础篇 02 StoryBoard 故事板文件

    iOS基础 02 StoryBoard 故事板文件 目录: 1. 故事板的导航特点 2. 故事板中的Scene和Segue 3. 本文最后 在上篇HelloWorld工程中有一个Main.storyb ...

  6. [IOS 开发] TableView、多个TableViewCell、自定义Cell、Cell上画画(故事板+代码方式)

    第一步: //UserTableViewCell.h这里定义第一种Cell #import <UIKit/UIKit.h> @interface UserTableViewCell : U ...

  7. [Swift实际操作]八、实用进阶-(8)使用performSegue在故事板页面之间进行数据传递

    本文将演示故事板页面之间的数据传递.首先在一个空白项目中,打开项目自带的故事板文件(Main.storyboard).故事板中已经拥有了一个视图控制器,点击选择该视图控制器.然后依此点击[Editor ...

  8. [Xcode 实际操作]九、实用进阶-(22)Storyboard故事板的常用布局结构

    目录:[Swift]Xcode实际操作 本文将演示如在使用故事板搭建项目时,常用的一种故事板布局结构. 在项目导航区,打开故事板文件[Main.storyboard] 当前故事板中只有一个视图控制器控 ...

  9. [Xcode 实际操作]九、实用进阶-(23)多个Storyboard故事板中的页面跳转

    目录:[Swift]Xcode实际操作 本文将演示多个Storyboard故事板中的页面跳转. 使用快捷键[Command]+[N]创建一个新的故事板文件. (在项目文件夹[DemoApp]上点击鼠标 ...

随机推荐

  1. ArcGIS案例学习笔记4_2

    ArcGIS案例学习笔记4_2 联系方式:谢老师,135_4855_4328,xiexiaokui#qq.com 时间:第4天下午 案例1:批量水文分析地理建模 数据:实验数据\Chp11\tutor ...

  2. Python 实践项目 游戏

    https://www.zhihu.com/collection/92700207?page=1

  3. git hg提交拉取

    工作总结web_acl 535 git clone “ssh://git@outergit.yonyou.com:49622/esn_web/web_acl.git" 600 git bra ...

  4. swagger ui

    You can pull a pre-built docker image of the swagger-ui directly from Dockerhub: docker pull swagger ...

  5. mysql中binlog_format的三种模式

    mysql复制主要有三种方式:基于SQL语句的复制(statement-based replication, SBR),基于行的复制(row-based replication, RBR),混合模式复 ...

  6. Cache Server

    [Cache Server] Whenever a source Asset like a .psd or an .fbx file is modified, Unity detects the ch ...

  7. 在winsshd 中添加id_rsa.pub 实现Windows 服务器主机自动信任Linux 客户端

    文章一. 生成密钥: 在Linux主机(ssh客户端),通过ssh-keygen在建立SSH keys# ssh-keygen -t rsa (连续三次回车,即在本地生成了公钥和私钥,不设置密码)将在 ...

  8. oracle中的分支与循环语句

    分支语句 if的三种写法一, if 2 < 1 then dbms_output.put_line('条件成立'); end if; 二, if 2 < 1 then dbms_outpu ...

  9. python之socket运用之执行命令

    服务端的代码 import socket import subprocess HOST = "127.0.0.1" PORT = 5001 ip_bind = (HOST,PORT ...

  10. Course Schedule课程表12(用Topological Sorting)

    [抄题]: 现在你总共有 n 门课需要选,记为 0 到 n - 1.一些课程在修之前需要先修另外的一些课程,比如要学习课程 0 你需要先学习课程 1 ,表示为[0,1]给定n门课以及他们的先决条件,判 ...