让我们回到游戏排行窗口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. python处理分隔大文件

    4个.sql格式的文件,2G大小,直接插入mysql数据中,文件太大了,导入不进去. 太大的文件用python处理也很麻烦,处理不了,只能先分隔成小文件处理. 文件中数据格式:其中values里面的数 ...

  2. 【Java】JVM(四)、虚拟机参数配置

    1. -Xms20M      JVM启动时候的内存大小为20M   2. -Xmx20M     JVM内存最大值是20M 将其与Xms大小一致可以避免JVM内存自动扩展   3. -Xss128K ...

  3. 用R进行统计学分析

    1.基本统计 summary函数:R中的summary函数根据输入的类提供输入的摘要.该函数根据输入对象的类调用各种函数.返回值也取决于输入对象.例如,如果输入是一个由数字数据组成的向量,它将为数据提 ...

  4. CentOS6.5下安装ActiveMQ

    1.下载ActiveMQ [root@localhost softwares]# wget http://archive.apache.org/dist/activemq/apache-activem ...

  5. php拓展安装

    Yaf安装配置:http://www.laruence.com/manual/yaf.install.html#yaf.installation.linux 下载Yaf的最新版本, 解压缩以后, 进入 ...

  6. ECMAScript6新特性之Reflect

    一 Reflect.ownKeys()获取对象属性. 可枚举的.不可枚举的.自有的.继承的. let fruit = { '2' : 'mango', [Symbol.for('pink')] : ' ...

  7. linux中使用locate搜索文件方法记录

    在linux中,有时用apt或者yum等软件包管理工具直接安装软件的时候,不知道软件到底安装到哪里去了,配置文件放哪里?这个时候就可以使用搜索命令locate来找到这些文件.海词上locate翻译为找 ...

  8. day6:vcp考试

    Q101. Refer to the Exhibit.Which tab shows the Hardware Acceleration support status?A. DevicesB. Pro ...

  9. 在Objc项目中调用Swift

    之前的文字中记录了在Swift项目中调用OC的相关代码,比较简单直接 传送门 但是在OC中调用swift代码则不是那么的和谐,网络上很多文章业已经有点陈旧.记录步骤如下: 1.创建OC项目 (1)启动 ...

  10. hadoop应用场景总结

    原文地址 我个人接触hadoop仅仅不到一年,因为是业余时间学习,故进度较慢,看过好多视频,买过好多书,学过基本知识,搭建过伪分布式集群,有过简单的教程式开发,恰逢毕业季,面试过相关岗位,自认为路还很 ...