让我们回到游戏排行窗口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. hibernate 1对1的关系

    hibernate 中1对1的关系分为外键关联和主键关联 外键关联: //多方 public class Students {                                     ...

  2. DESeq2包

    1)简介: DESeq2-package: for differential analysis of count data(对count data 做差异分析) 2)安装 if("DESeq ...

  3. Linux Tomcat重新启动

    在Linux系统下,重启Tomcat使用命令操作的! 首先,进入Tomcat下的bin目录 cd /usr/local/tomcat/bin 使用Tomcat关闭命令 ./shutdown.sh 查看 ...

  4. 1D Blending

    [1D Blending] BlendTree有类型之分,分为1D.2D.本文记录1D. 1D Blending blends the child motions according to a sin ...

  5. overflow: scroll

    overflow: scroll在安卓5.0的情况下,不论内容是否填满屏幕,都会强制解析出滚动条,所以最好是使用overflow: auto

  6. hdoj1114 Piggy-Bank(DP 完全背包)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1114 思路: 题目看着有些绕,其实就是完全背包的变形,需要注意的是这里求最小值,所以需要将dp数组初始 ...

  7. 微信公众平台开发之基于百度 BAE3.0 的开发环境搭建(采用 Baidu Eclipse)

    3.通过 SVN 检入工程     在 bae 上的应用添加部署成功后,如图 7     点击“点击查看”按钮,会打开一个新页面,页面上会打印 “hello world” ,这是因为我们的应用包含有示 ...

  8. IIS7web服务器调试ASP.net程序遇到的一些故障的解决办法

    1. [由于 Web 服务器上的“ISAPI 和 CGI 限制”列表设置,无法提供您请求的页面] 故障描述:[由于 Web 服务器上的“ISAPI 和 CGI 限制”列表设置,无法提供您请求的页面] ...

  9. 翻转链表reverse linked list:全部,m~n

    全部 [抄题]: Reverse a singly linked list. [思维问题]: 以为要用dummy node [一句话思路]: 直接全部转过来就行了,用dummy node反而多余 [输 ...

  10. MongoDB的文档、集合、数据库(二)

    为了理解MongoDB的名词,可以将其于关系型数据库进行对比: 一.文档 概述 文档是MongoDB的核心概念,是数据的基本单元,非常类似于关系数据库中的行.在MongoDB中,文档表示为键值对的一个 ...