0

0

iOS 5 故事板入门(4)

黄舟

黄舟

发布时间:2017-01-20 10:26:28

|

1406人浏览过

|

来源于php中文网

原创

让 addplayer 窗口动起来

现在,我们先忽略“Game”行,只考虑用户输入的玩家名称。
当用户触摸 Cancel 按钮,窗口会关闭,用户输入的数据都将丢失。这部分是 OK 的。在委托对象(Players 窗口)收到“didcancel”消息时,会简单地关闭 AddPlayer 窗口。

当用户触摸 done 按钮时,我们应该创建新的 Player 对象并设置它的属性。然后告诉委托对象我们添加了新的玩家,他应该刷新界面。

因此, PlayerDetailsViewController.m中的 done 方法变成:

- (IBAction)done:(id)sender {
        Player *player = [[Player alloc] init];
        player.name = self.nameTextField.text;
        player.game = @"Chess";
        player.rating = 1;
        [self.delegate playerDetailsViewController:self         didAddPlayer:player];
}

   

我们需要导入头文件:

#import "Player.h"

   

done 方法现在创建了新的 Player对象并将其发送给委托对象。当前在PlayerDetailsViewController.h文件中的委托协议中的需要修改为:

@class Player;  
@protocol PlayerDetailsViewControllerDelegate <NSObject>
- (void)playerDetailsViewControllerDidCancel:   (PlayerDetailsViewController *)controller;
- (void)playerDetailsViewController:   (PlayerDetailsViewController *)controller    didAddPlayer:(Player *)player;
@end

   

didSave 方法定义去掉了,我们用一个 didAddPlayer 方法代替了。接下来的时期是在

 PlayersViewController.m 中实现该方法:

  - (void)playerDetailsViewController:   (PlayerDetailsViewController *)controller    didAddPlayer:(Player *)player{
        [self.players addObject:player];
        NSIndexPath *indexPath =       [NSIndexPath indexPathForRow:[self.players count] - 1         inSection:0];
        [self.tableView insertRowsAtIndexPaths:       [NSArray arrayWithObject:indexPath]         withRowAnimation:UITableViewRowAnimationAutomatic];
        [self dismissViewControllerAnimated:YES completion:nil];
}

   

首先向 players 数组中加入新的 Player 对象。然后告诉table view 增加新的行(在底部),因为 table view 和 数据源必须保持同步。我们也可以用[self.tableView reloadData] ,但以动画的方式插入一个新行显得更好。UITableViewRowAnimationAutomatic是 iOS 5 中出现的新常量,它会自动选择恰当的动画,这取决于你在何处插入行,非常方便。

运行程序,你应该可以加入新的玩家到列表中了!

你可能会怀疑故事板的性能会有问题。但一次加载一整个 storyboard 没什么了不起的。故事板并不会直接初始化所有的viewController,它只初始化 initial view controller。因为我们的 initial view controller s是一个TabBarController,所以它包含的两个 view controller 也被加载(Players View Controller和第二个viewcontroller)。

其他的 ViewController 不会被初始化,一直到你 “segue到”它们。一旦你关闭这些 ViewController,它们就被释放,因此只有当前使用的 ViewController 会呆在内存中,就如你曾经使用的一个个的nib 文件。

让我们做个实验。将这个方法加到 PlayerDetailsViewController.m 中:

- (id)initWithCoder:(NSCoder *)aDecoder {
        if ((self = [super initWithCoder:aDecoder])) {
               NSLog(@"init PlayerDetailsViewController");
        }
        return self;
 }
- (void)dealloc {
        NSLog(@"dealloc PlayerDetailsViewController");
}

   

我们覆盖了 initWithCoder 和 dealloc方法以输出一些信息。再次运行程序,打开 Add Player 窗口。可以看到 ViewController 直至此时才 alloc。关闭窗口(Cancel 或 Done 按钮),可以看到delloc 输出的内容。再次打开窗口,也可以看到 initWithCoder 输出的内容。这充分说明 Viewcontroller 是按需加载的,就如使用nib 方式一般。

还有一点,静态 cell 只能用于 UITableViewController。故事板编辑器允许你在一般的UIViewController 中的 TableView 上使用静态 cell,但在运行时就不行了。原因是 UITableViewController 为静态cell 提供了特殊的数据源机制。Xcode 甚至会发出警告,禁止你编译此类项目:

 “Illegal Configuration: Statictable views are only valid when embedded in UITableViewController instances”(静态的 table view 只对 UITableViewController 有效)。

模板 cell 则可以在常规的 ViewController 中使用。当然IB 中不行。因此,如果你想使用模板 cell 和静态 cell,都必须使用故事板。

你可能想同时在一个 table view 中使用静态 cell 和动态cell,但 SDK 不支持。如果你确实想这样做,请参考 这里。

注意:如果你的窗口有太多的静态 cell ——超过了屏幕所能容纳的空间——你可以在故事板编辑器中通过鼠标或者触控板的滚动手势来股东视图。这个功能不是很直观,但很有用。

Game Picker 窗口

轻击 Add Player 窗口上的 Game 单元格,会打开一个列表,让用户选择游戏。因此需要增加一个Table View Controller。这次,我们用 Push 的方式而不是 Modal 的方式。

拖一个新的 Table View Controller 到故事板。从Add Player 窗口中选择 Game 单元格(注意选中整个 cell,而不是选中上面的 Lable),按住 ctrl 键,拖到新的TableViewController 上。这将创建一个 segue。选择 Push 类型的 segue,并命名该 segue

为 “PickGame”。

双击导航条,将标题修改为“Choose Game”。模板 cell 的style 设置为 Basic,重用 ID 设置为“ GameCell”,如下图所示:

472.jpg

新建一个 UITableViewController 子类,名为GamePickerViewController。别忘了将故事板中的 TableViewController 的 Identifier 设置为 GamePickerViewController.

先让我们为新场景准备一些数据。在GamePickerViewController.h 中添加实例变量:

@interface GamePickerViewController : UITableViewController {
     NSArray * games;
}

   

在 GamePickerViewController.m 的viewDidLoad 方法中:

- (void)viewDidLoad {
        [super viewDidLoad];
        games = [NSArray arrayWithObjects:
              @"Angry Birds",
              @"Chess",
              @"Russian Roulette",
              @"Spin the Bottle",
              @"Texas Hold’em Poker",
              @"Tic-Tac-Toe",
              nil];
}

   

因为在 viewDidUnload 中初始化games 数组,所以必须在 viewDidUnload 中进行释放:

- (void)viewDidUnload {
        [super viewDidUnload];
        games = nil;
}

   

尽管这个窗口的 viewDidUnload实际上永远不会调用(我们永远也不会用另一个视图来覆盖它),但保持 release/alloc 平衡是一种良好的做法。

修改 TableView 的数据源方法:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
        return 1;
}
- (NSInteger)tableView:(UITableView *)tableView    numberOfRowsInSection:(NSInteger)section {
        return [games count];
}  
- (UITableViewCell *)tableView:(UITableView *)tableView    cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        UITableViewCell *cell = [tableView       dequeueReusableCellWithIdentifier:@"GameCell"];
        cell.textLabel.text = [games objectAtIndex:indexPath.row];
        return cell;
}

   

关于数据源就这么多。运行程序,轻击 Game 行,Choose Game 窗口会出现。点击任何行都不会有任何动作。当然,由于窗口是以Push 方式呈现的,你可以点击导航栏的返回按钮返回 Add Player 窗口。

473.jpg

这很好,不是吗?我们不需要写任何呈现新窗口的代码。只是从静态 cell 拖拽了一条线到新窗口而已。(注意当你点击Game 行是, table view 的委托方法即PlayerDetailsViewController的 didSelectRowAtIndexPath是会被调用的,请不要在其中加入任何代码,以免造成混乱)。

沙之丘企业网站程序3.5
沙之丘企业网站程序3.5

沙之丘企业网站程序是一个以asp.net(C#) 4.0 +access进行开发的企业网站源码。主要功能:1、产品、设备、新闻系统2、留言信息直接发邮件到相关部门3、所有链接都以一级目录显示更好的权重4、其他信息扩展,可以增加如:人事招聘,公司介绍,地图,联系我们等5、带有商品和设备的搜索功能6、模板动态化方便扩展模板7、简体繁体选择显示运行环境:windows 2003或者更高windows服务

下载

当然,目前新窗口还没有什么作用。 我们必须在 GamePickerViewController.h中定义一个新的委托协议使他能返回一些数据:

@class GamePickerViewController;  
@protocol GamePickerViewControllerDelegate <NSObject>
- (void)gamePickerViewController:   (GamePickerViewController *)controller    didSelectGame:(NSString*)game;
@end  
@interface GamePickerViewController : UITableViewController  
@property (nonatomic, weak) id <GamePickerViewControllerDelegate> delegate;
@property (nonatomic, strong) NSString *game;  
@end

   

修改 GamePickerViewController.m为:

@implementation GamePickerViewController {
        NSArray *games;
        NSUInteger selectedIndex;
}
@synthesize delegate;
@synthesize game;

添加了一个新的实例变量 selectedIndex,以及属性合成语句。

在 viewDidLoad 方法末增加:

selectedIndex = [games indexOfObject:self.game];

   

用户选定的游戏名称将保存到 self.game 中。selectedIndex则是 game 位于 games 数组中的索引。该索引用于在 TableView 中用一个对钩图标来标出所选 cell。因此,self.game 必须在视图加载之前就必须指定一个值。这可以通过prepareForSegue 方法进行,这个方法在 viewDidLoad 之前调用。

修改 cellForRowAtIndexPathto 方法为:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        UITableViewCell *cell = [tableView       dequeueReusableCellWithIdentifier:@"GameCell"];
        cell.textLabel.text = [games objectAtIndex:indexPath.row];
        if (indexPath.row == selectedIndex)
               cell.accessoryType =            UITableViewCellAccessoryCheckmark;
        else
               cell.accessoryType = UITableViewCellAccessoryNone;
        return cell;
}

   

在当前选定的游戏名称后面标志一个对钩图标。这对于用户来说应该是需要的。

修改 didSelectRowAtIndexPath方法如下:

- (void)tableView:(UITableView *)tableView    didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
        [tableView deselectRowAtIndexPath:indexPath animated:YES];
        if (selectedIndex != NSNotFound)      {
               UITableViewCell *cell = [tableView            cellForRowAtIndexPath:[NSIndexPath              
               indexPathForRow:selectedIndex inSection:0]];
               cell.accessoryType = UITableViewCellAccessoryNone;
        }
        selectedIndex = indexPath.row;
        UITableViewCell *cell =       [tableView cellForRowAtIndexPath:indexPath];
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
        NSString *theGame = [games objectAtIndex:indexPath.row];
        [self.delegate gamePickerViewController:self       didSelectGame:theGame];
}

   

首先反选改行。这将使单元格从高亮的蓝色变回正常的白色。然后移掉或添加单元格末尾的对钩标志(依赖于选中状态)。最终,将用于已选的游戏名称通过委托协议返回给委托对象。

运行程序进行测试。从游戏列表中点击一个游戏。该行末自动添加一个对钩。点击另一个游戏名称,对钩随之移动到新的选择。当你点击行时,窗口应该关闭,但什么也没发生。因为实际上我们还没有将委托对象连接进来。

#import "GamePickerViewController.h"

   

在 @interface 语句中添加协议:

@interface PlayerDetailsViewController : UITableViewController <GamePickerViewControllerDelegate>
   
在 PlayerDetailsViewController.m,添加prepareForSegue 方法:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
        if ([segue.identifier isEqualToString:@"PickGame"]) {
               GamePickerViewController *gamePickerViewController =            segue.destinationViewController;
               gamePickerViewController.delegate = self;
               gamePickerViewController.game = game;
        }
}

   

就如我们前面所做的一样。这次的目标场景是 game picker 窗口。记住,该方法发生在GamePickerViewController 被初始化之后,视图被加载之前。

 “game” 变量是新加的。它是一个实例变量:

@implementation PlayerDetailsViewController {
        NSString *game;
}

   

该变量用于存储用户选定的游戏,因此我们可以把它放到 Player 对象中。我们可以给它一个默认值。通过initWithCoder 方法是个不错的选择:

- (id)initWithCoder:(NSCoder *)aDecoder {
        if ((self = [super initWithCoder:aDecoder])) {
               NSLog(@"init PlayerDetailsViewController");
               game = @"Chess";
        }
        return self;
}

   

如果你用过 nib 文件,initWithCoder 你应该很熟悉。在故事板中,这些方法同样存在:initWithCoder、awakeFromNib和viewDidLoad。你可以把故事板看成是 nib 文件的集合,再加上它们之间的转换和关系。但是故事板中的 view controller 仍然是用同样的方法编码和解码。

修改 viewDidLoad ,让 Game 单元格显示游戏名:

- (void)viewDidLoad {
        [super viewDidLoad];
        self.detailLabel.text = game;
}

接下来实现委托方法:

#pragma mark - GamePickerViewControllerDelegate  
- (void)gamePickerViewController:   (GamePickerViewController *)controller    didSelectGame:(NSString*)theGame {
        game = theGame;
        self.detailLabel.text = game;
        [self.navigationController popViewControllerAnimated:YES];
}

很简单,将新游戏名称赋给我们的实例变量 game 以及 cell 的Label,然后关闭游戏选择窗口。由于它是一个 Push 式 segue,从导航控制器的栈顶中将它弹出即可。

在 done 方法中,我们将已选游戏放入 Player 对象:

- (IBAction)done:(id)sender {
        Player *player = [[Player alloc] init];
        player.name = self.nameTextField.text;
        player.game = game;
        player.rating = 1;
        [self.delegate playerDetailsViewController:self didAddPlayer:player];
}

好了,我们的游戏选择窗口就完成了!

接下来的内容

这是 示例工程源代码。

恭喜你,现在你已经基本掌握了故事板的使用,能够用 segue 在多个view controller 中导航及转换。

关于更多 iOS 5 中故事板的学习内容,请查看我们的新书iOS 5 教程,其中还包含了:

  • 修改 PlayerDetailsViewController 以便对 Player 对象进行编辑。

  • 如何创建多个outgoing segue,如何使你的 ViewController 能够复用,能处理多个 incoming segue。

  • 如何从 disclosure 按钮、手势及任意事件调用 segue。

  • 定制 segue——而不仅仅使用标准的Push/Modal式动画!

  • 如何在 iPad 中使用故事板,包括 split-view controller 以及 popover。

  • 最后,如何手动加载故事板,并在app 中使用多个故事板。

以上就是iOS 5 故事板入门(4)的内容,更多相关内容请关注PHP中文网(www.php.cn)!

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法

本专题系统整理pixiv网页版官网入口及登录访问方式,涵盖官网登录页面直达路径、在线阅读入口及快速进入方法说明,帮助用户高效找到pixiv官方网站,实现便捷、安全的网页端浏览与账号登录体验。

705

2026.02.13

微博网页版主页入口与登录指南_官方网页端快速访问方法
微博网页版主页入口与登录指南_官方网页端快速访问方法

本专题系统整理微博网页版官方入口及网页端登录方式,涵盖首页直达地址、账号登录流程与常见访问问题说明,帮助用户快速找到微博官网主页,实现便捷、安全的网页端登录与内容浏览体验。

233

2026.02.13

Flutter跨平台开发与状态管理实战
Flutter跨平台开发与状态管理实战

本专题围绕Flutter框架展开,系统讲解跨平台UI构建原理与状态管理方案。内容涵盖Widget生命周期、路由管理、Provider与Bloc状态管理模式、网络请求封装及性能优化技巧。通过实战项目演示,帮助开发者构建流畅、可维护的跨平台移动应用。

117

2026.02.13

TypeScript工程化开发与Vite构建优化实践
TypeScript工程化开发与Vite构建优化实践

本专题面向前端开发者,深入讲解 TypeScript 类型系统与大型项目结构设计方法,并结合 Vite 构建工具优化前端工程化流程。内容包括模块化设计、类型声明管理、代码分割、热更新原理以及构建性能调优。通过完整项目示例,帮助开发者提升代码可维护性与开发效率。

22

2026.02.13

Redis高可用架构与分布式缓存实战
Redis高可用架构与分布式缓存实战

本专题围绕 Redis 在高并发系统中的应用展开,系统讲解主从复制、哨兵机制、Cluster 集群模式及数据分片原理。内容涵盖缓存穿透与雪崩解决方案、分布式锁实现、热点数据优化及持久化策略。通过真实业务场景演示,帮助开发者构建高可用、可扩展的分布式缓存系统。

61

2026.02.13

c语言 数据类型
c语言 数据类型

本专题整合了c语言数据类型相关内容,阅读专题下面的文章了解更多详细内容。

30

2026.02.12

雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法
雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法

本专题系统整理雨课堂网页版官方入口及在线登录方式,涵盖账号登录流程、官方直连入口及平台访问方法说明,帮助师生用户快速进入雨课堂在线教学平台,实现便捷、高效的课程学习与教学管理体验。

15

2026.02.12

豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法
豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法

本专题汇总豆包AI官方网页版入口及在线使用方式,涵盖智能写作工具、图片生成体验入口和官网登录方法,帮助用户快速直达豆包AI平台,高效完成文本创作与AI生图任务,实现便捷智能创作体验。

669

2026.02.12

PostgreSQL性能优化与索引调优实战
PostgreSQL性能优化与索引调优实战

本专题面向后端开发与数据库工程师,深入讲解 PostgreSQL 查询优化原理与索引机制。内容包括执行计划分析、常见索引类型对比、慢查询优化策略、事务隔离级别以及高并发场景下的性能调优技巧。通过实战案例解析,帮助开发者提升数据库响应速度与系统稳定性。

58

2026.02.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Excel 教程
Excel 教程

共162课时 | 18.1万人学习

Uniapp从零开始实现新闻资讯应用
Uniapp从零开始实现新闻资讯应用

共64课时 | 6.8万人学习

Uniapp180分钟快速入门
Uniapp180分钟快速入门

共25课时 | 4.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号