通常有两种方式可以在应用运行时为UITableView对象增加行。
•在表视图上方放置添加按钮。如果数据的字段较多,需要显示一个用于输入的详细视图,就可以使用这种方式。例如,在iOS自带的通讯录(Contacts)应用中,点击添加按钮可以进入添加新联系人的详细视图并输入该联系人的信息。
•在UITableViewCell对象左边显示一个绿色加号按钮。在为数据添加一个新字段时可以使用这种方式。例如,在联系人应用中需要为联系人添加生日信息,可以在编辑模式中点击“add birthday(添加生日)”左边的绿色加号按钮。
本节采用的则是另一种方式:在headerView中放置一个标题为New的按钮。当用户按下这个按钮时,就为UITableView对象添加一新行。在BNRItemsViewController.m中实现addNewItem:,代码如下:
- (IBAction)addNewItem:(id)sender
{
// 创建NSIndexPath对象,代表的位置是:第一个表格段,最后一个表格行
NSInteger lastRow = [self.tableView numberOfRowsInSection:0];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:lastRow inSection:0];
//将新行插入UITableView对象
[self.tableView insertRowsAtIndexPaths:@[indexPath]
withRowAnimation:UITableViewRowAnimationTop];
}
创建NSIndexPath对象,代表的位置是:第一个表格段,最后一个表格行将新行插入UITableView对象构建并运行应用。按下New按钮,Homepwner会崩溃。查看控制台输出,可以发现崩溃原因是应用抛出了内部冲突异常(internal inconsistency exception)。
这是因为任何一个UITableView对象都要从其数据源获取需要显示的数据,数据源决定UITableView对象需要显示的行数。addNewItem:方法为UITableView对象增加了一新行,使行数从原来的5行增加到了6行。接着,当UITableView对象再次访问其数据源,获取应该显示的行数时,BNRItemsViewController对象会查询BNRItemStore对象并返回allItems数组所包含的指针个数。因为allItems数组没有发生变化,仍旧是5个,所以UITableView对象会由于两者的个数不同而抛出内部冲突异常。
添加表格行时,必须确保UITableView对象当前显示的行数与数据源提供的行数相同。为此,必须在添加新行前,创建一个新的BNRItem对象并加入BNRItemStore。
在BNRItemsViewController.m的addNewItem:方法中添加以下代码:
- (IBAction)addNewItem:(id)sender
{
NSInteger lastRow = [self.tableView numberOfRowsInSection:0];
// 创建新的BNRItem对象并将其加入BNRItemStore对象
BNRItem *newItem = [[BNRItemStore sharedStore] createItem];
// 获取新创建的对象在allItems数组中的索引
NSInteger lastRow = [[[BNRItemStore sharedStore] allItems] indexOfObject:newItem];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:lastRow inSection:0];
//将新行插入UITableView对象
[self.tableView insertRowsAtIndexPaths:@[indexPath]
withRowAnimation:UITableViewRowAnimationTop];
}
构建并运行应用。按下New按钮,UITableView对象会以动画的形式将新创建的行移入表格的末端。这里需要重点指出,视图的责任是将模型对象中的数据呈现给用户,只更新视图而不更新模型对象就会发生数据不一致的错误。
这段代码调用了BNRItemsViewController对象的tableView方法以获取表视图。BNRItemsViewController对象的tableView方法继承自UITableViewController,会返回UITableViewController对象的表视图。虽然也可以向UITableViewController对象发送view消息,并且得到同一个对象,但是因为tableView方法的返回类型是指向UITableView对象的指针,所以可以将其返回的指针直接赋给UITableView类型的指针变量,以便向视图发送UITableView特有的消息(例如insertRowsAtIndexPaths: withRowAnimation:),避免Xcode发出警告信息。
因为可以通过New按钮来为UITableView对象增加新行,所以在BNRItemsView- Controller.m的init方法中删除使用BNRItemStore创建5个随机BNRItem对象的代码:
- (instancetype)init
{
// 调用父类的指定初始化方法
self = [super initWithStyle:UITableViewStylePlain];
if (self) {
for (int i = 0; i < 5; i++) {
[[BNRItemStore sharedStore] createItem];
}
}
return self;
}
构建并运行应用。UITableView对象不会显示任何内容。按下New按钮,可以为UITableView对象增加新行。