首页 » iOS编程(第4版) » iOS编程(第4版)全文在线阅读

《iOS编程(第4版)》9.1 编辑模式

关灯直达底部

UITableView有一个名为editing的属性,如果将editing属性设置为YES,UITableView就会进入编辑模式。在编辑模式下,用户可以管理UITableView中的表格行,例如之前提到的添加、删除和移动等操作。但是编辑模式没有提供修改行的内容的功能。

首先需要更新界面,使用户可以将UITableView对象设置为编辑模式。本章是为UITableView对象的表头视图(header view)增加一个按钮,然后通过点击按钮使UITableView对象进入或退出编辑模式。表头视图是指UITableView对象可以在其表格上方显示的特定视图,适合放置针对某个表格段或整张表格的标题和控件。表头视图可以是任意的UIView对象。

表头视图有两种,分别针对表格段和表格。类似地,还有表尾视图(footer view),也具有表格段和表格两种(见图9-2)。

图9-2 针对表格段的表头视图和表尾视图

接下来创建一个针对表格的表头视图。这个表头视图包含两个UIButton对象,其中一个负责切换UITableView对象的编辑模式,另一个负责创建新的BNRItem对象并加入UITableView对象。可以使用代码创建这个表头视图及其包含的子视图,但是本章将使用XIB文件创建它们。之后,BNRItemsViewController对象在需要显示表头视图时会加载相应的XIB文件。

首先需要编写一些代码。重新打开第8章中的Homepwner.xcodeproj,在BNRItemsViewController.m中添加BNRItemsViewController的类扩展,然后声明一个插座变量并添加两个新方法,代码如下:

@interface BNRItemsViewController

@property (nonatomic, strong) IBOutlet UIView *headerView;

@end

@implementation BNRItemsViewController

// 这里省略了其他方法

- (IBAction)addNewItem:(id)sender

{

}

- (IBAction)toggleEditingMode:(id)sender

{

}

载入XIB文件后,headerView会指向XIB文件中的顶层对象,并且是强引用。指向顶层对象的插座变量必须声明为强引用;相反,当插座变量指向顶层对象所拥有的对象(例如顶层对象的子视图)时,应该使用弱引用。

下面创建一个新的XIB文件。和本书之前创建的XIB文件不同,这个XIB文件和视图控制器的视图无关(BNRItemsViewController作为UITableViewController的子类,可以自行创建其视图)。通常情况下,可以用XIB文件来创建某个视图控制器的视图。但是,也可以在XIB文件中随意创建多个视图并设置层级结构和布局,然后在运行应用时按需载入。

选择File菜单中的New菜单项,然后选择File…选中窗口左侧iOS部分的User Interface,然后选中窗口右侧的Empty模板,最后单击Next按钮(见图9-3)。

图9-3 创建新的XIB文件

在新出现的面板中选择Device Family下拉菜单中的iPhone,单击Next按钮。Xcode会提示保存文件,将文件名设置为HeaderView.xib,单击Save按钮。

选中HeaderView.xib中的File/'s Owner,打开标识检视面板,将Class文本框中的UIViewController修改为BNRItemsViewController(见图9-4)。

图9-4 修改File/'Owner

先拖曳一个UIView对象至画布,然后再拖曳两个UIButton对象至这个UIView对象。现在需要调整UIView对象的大小,但是读者会发现UIView对象的大小被锁定了,无法调整,因此需要解除大小锁定。选中UIView对象并打开属性检视面板,在Simulated Metrics部分中点击标题为Size的选项列表,选择None(见图9-5)。

图9-5 解除视图大小锁定

现在可以调整视图大小了,请按图9-6所示调整UIView对象的大小并创建相应的关联。

图9-6 HeaderView.xib的布局

还要将UIView对象的背景颜色修改为全透明颜色。具体做法为:选中之前加入的UIView对象并打开属性检视面板。单击标题为Background的颜色的选项列表,选择Clear Color(见图9-7)。

图9-7 将背景色设置为Clear Color

本书之前所创建的XIB文件,都是通过UIViewController类的默认实现自动载入的。以第6章的BNRReminderViewController为例,因为它是UIViewController的子类,所以BNRReminderViewController对象会在需要显示其视图时,自动载入BNRReminderViewController.xib。但是对于HeaderView.xib,则需要编写特定的代码,让BNRItemsViewController对象能够“手动”载入该XIB文件。

使用NSBundle类可以载入指定的XIB文件。该类是“应用程序包”和“应用程序包所包含的可执行文件”之间的接口。通过该类,应用可以访问某个程序包中的文件。向该类发送mainBundle消息可以得到指向主NSBundle对象的指针,该对象是应用在启动时创建的。

得到主NSBundle对象后,就可以要求其载入应用程序包中的某个XIB文件。在BNRItemsViewController.m中实现headerView方法,代码如下:

- (UIView *)headerView

{

// 如果还没有载入headerView...

if (!_headerView) {

// 载入HeaderView.xib

[[NSBundle mainBundle] loadNibNamed:@/"HeaderView/"

owner:self

options:nil];

}

return _headerView;

}

该方法使用了一种名为延迟实例化(Lazy Instantiation)的设计模式:只会在真正需要使用某个对象时再创建它。在某些情况下,这种设计模式可以显着减少内存占用。

调用loadNibNamed:owner:options:时,需要传入XIB文件的文件名。文件名不需要包含后缀,NSBundle会自行判断并处理。此外,这段代码将self作为owner实参(拥有者)传给了NSBundle对象,目的是当BNRItemsViewController对象将XIB文件加载为NIB文件时,使用BNRItemsViewController对象自身替换占位符对象File/'s Owner。

BNRItemsViewController对象会在第一次收到headerView消息时载入HeaderView.xib,然后为插座变量headerView赋值,并将其指向HeaderView.xib中的顶层UIView对象。当用户按下这个顶层UIView对象中的任何一个按钮时,BNRItemsViewController对象都会收到指定的动作消息。

加载了headerView后,还需要将其设置为UITableView对象的表头视图。在BNRItemsViewController.m的viewDidLoad方法中,添加以下代码:

- (void)viewDidLoad

{

[super viewDidLoad];

[self.tableView registerClass:[UITableViewCell class]

forCellReuseIdentifier:@/"UITableViewCell/"];

UIView *header = self.headerView;

[self.tableView setTableHeaderView:header];

}

构建并运行应用,UITableView对象会在列表上方显示两个按钮。

XIB文件不仅可以用来创建视图控制器的视图(例如BNRReminderView- Controller.xib),也可以用来固化其他的视图层次结构(例如HeaderView.xib)。实际上,任何对象都可以通过向主NSBundle对象发送loadNibNamed:owner:options:消息手动载入XIB文件。

前面介绍过,UIViewController默认已经实现了通过XIB文件载入视图的功能。其实现原理和headerView方法的相同,代码也相似。唯一的差别是,UIViewController对象会在载入XIB文件后,将插座变量view关联至XIB文件中指定的UIView对象。UIViewController的loadView方法的代码示例如下(这里列出的代码仅是举例,和UIViewController的loadView方法的实际代码不同):

- (void)loadView

{

// NIB文件位于哪个bundle中?

// 是否为initWithNibName:bundle:方法传了bundle参数?

NSBundle *bundle = [self nibBundle];

if (!bundle) {

// 使用默认bundle

bundle = [NSBundle mainBundle];

}

// NIB文件的名称是什么?

// 是否为initWithNibName:bundle:方法传了NIB文件名参数?

NSString *nibName = [self nibName];

if (!nibName) {

// 使用默认NIB文件名

nibName = NSStringFromClass([self class]);

}

// 尝试在bundle中查找默认NIB文件

NSString *nibPath = [bundle pathForResource:nibName

ofType:@/"nib/"];

// 该NIB文件是否存在?

if (nibPath) {

// 加载NIB文件(同时还会设置view插座变量)

[bundle loadNibNamed:nibName owner:self options:nil];

} else {

// 如果没有NIB文件,就创建一个空白视图

self.view = [[UIView alloc] init];

}

}

接下来实现toggleEditingMode:。虽然可以直接通过设置UITableView对象的editing属性来切换编辑模式,但是UITableViewController也有一个从UIViewController继承而来的editing属性。当某个UITableViewController对象的editing属性发生变化时,UITableViewController对象会同步修改其UITableView对象的editing属性。

向某个UIViewController对象或UIViewController子类对象发送setEditing: animated:消息,可以设置该对象的editing属性(UITableViewController覆盖了UIViewController的setEditing:animated:方法)。在BNRItemsViewController.m中实现toggleEditingMode:,代码如下:

- (IBAction)toggleEditingMode:(id)sender

{

// 如果当前的视图控制对象已经处在编辑模式…

if (self.isEditing) {

// 修改按钮文字,提示用户当前的表格状态

[sender setTitle:@/"Edit/" forState:UIControlStateNormal];

// 关闭编辑模式

[self setEditing:NO animated:YES];

} else {

// 修改按钮文字,提示用户当前的表格状态

[sender setTitle:@/"Done/" forState:UIControlStateNormal];

// 开启编辑模式

[self setEditing:YES animated:YES];

}

}

构建并运行应用,按下Edit按钮,UITableView对象会开启编辑模式(见图9-8)。

图9-8 编辑模式下的UITableView对象