我有一个UITableView,我从来不想让它少于1个单元格:它是一个目录读数,如果目录中没有文件,它就有一个单元格,上面写着“没有文件”。(在编辑模式中,有一个用于创建文件的奖励单元格,因此编辑模式永远不会低于两个单元格。)
这可能只是因为缺乏睡眠,让我现在无法从纸袋中思考,但我总是被这样的错误所绊倒:
*** Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'Invalid update:
invalid number of sections. The number of sections contained
in the table view after the update (2) must be equal to
the number of sections contained in the table view before
the update (2), plus or minus the number of sections inserted
or deleted (1 inserted, 0 deleted).'
这是因为我在删除最后一个文件之前先发制人地添加了"No Files“占位符。如果我在添加占位符之前删除最后一个文件单元格,就会发生like崩溃。无论采用哪种方法,单元计数都会与numberOfRowsInSection返回的结果不同步,从而触发崩溃。
对于这种情况,肯定有一种设计模式。给我提供线索?
发布于 2013-05-08 12:32:25
我对此有另一个解决方案(似乎有很多方法可以做到这一点)。我发现其他可用的解决方案不适合我的需求,所以我想出了这个。
这种方法非常简单,对于那些想要使用两个(或更多)占位符单元格、不想打乱数据模型或多个表格视图、以及(尤其是)需要允许在表格中删除和插入单元格的人来说,这种方法非常好用。它还允许非常好的输入和输出转换,以显示和隐藏占位符。
听上去不错?好的!让我们来看看它的基本要点:
大多数占位符单元格解决方案的真正问题是,当您开始允许对表进行编辑(删除和插入)时,它们通常会失败。否则,您必须开始处理处理编辑的代码,这可能会使一切变得更加混乱。这里的问题通常是在numberOfRowsInSection
方法中返回不一致的值。tableview通常会遇到这样的问题:删除表格中的一个单元格,如果表格中只剩下一个单元格,并且在删除后还剩下一个单元格(或者插入时反之亦然)。
简单的解决方案是什么?我们总是有一个一致的单元格数量-数据源中的条目数量加上占位符的数量。占位符单元格总是存在的,它只是根据技术上是否应该存在来显示或隐藏它的内容。
尽管写得很长,但实现这一点实际上非常简单。让我们开始吧:
1.设置占位符单元原型:这非常简单。在情节提要中为占位符设置原型单元格。在我的例子中,我使用了两个:一个用来显示“正在加载...”当表格从服务器获取数据时,当表格中实际上没有任何内容时,显示“点击+上面添加项目”。
以您喜欢的方式设置您的单元格(我只是使用了副标题单元格样式,并将我的占位符文本放在了副标题标签中。如果这样做,不要忘记删除另一个标签的文本)。确保分配一个重用标识符,并将选择样式设置为“None”。
2.设置 numberOfRowsInSection
委托方法:这个方法现在将做两件基本的事情:第一是返回数据源中的行数(加上占位符的行数),第二是根据需要显示/隐藏占位符的文本。这是启动此操作的好地方,因为每次删除或插入单元格时都会调用此方法(实际上是两次;一次在编辑之前,一次在编辑之后)。为了避免每次调用方法时都运行动画,我们将使用BOOL placeholderIsHidden
来跟踪占位符的当前状态。我们还将在短暂的延迟后执行切换,以便启动单元格编辑动画。将此代码添加到您的类中:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
int count = [self.dataSource count];
// Hide/Show placeholder cell
if (count == 0) { // Placeholder should be shown
if (self.placeholderIsHidden) {
[self performSelector:@selector(animatePlaceholderCellChangeForIndexPath:) withObject:[NSIndexPath indexPathForRow:count inSection:0] afterDelay:0.1];
}
}
else { // Placeholder should be hidden
if (!self.placeholderIsHidden) {
[self performSelector:@selector(animatePlaceholderCellChangeForIndexPath:) withObject:[NSIndexPath indexPathForRow:count inSection:0] afterDelay:0.1];
}
}
return count + 1;
}
这就可以开始了!现在让我们添加animatePlaceholderCellChange
方法:
- (void)animatePlaceholderCellChangeForIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.8];
[UIView setAnimationDelegate:self];
[UIView setAnimationBeginsFromCurrentState:YES];
if (indexPath.row == 0) {
cell.detailTextLabel.hidden = NO;
self.placeholderIsHidden = NO;
}
else {
cell.detailTextLabel.hidden = YES;
self.placeholderIsHidden = YES;
}
[UIView commitAnimations];
}
此方法利用动画块来平滑显示和隐藏占位符之间的过渡(并使其与单元格编辑动画很好地配合)。
倒数第二,我们需要设置cellForRowAtIndexPath
方法来返回正确的单元格。
3. Setup cellForRowAtIndexPath
**:**这是相当简单的。将此代码放入您的方法中,在您的原型单元标识符声明之后,并在进行正常的单元设置之前:
// Add a placeholder cell while waiting on table data
int nodeCount = [self.dataSource count];
if (indexPath.row == nodeCount) {
// Place the appropriate type of placeholder cell in the first row
if (self.isLoading) {
return [tableView dequeueReusableCellWithIdentifier:LoadingCellIdentifier];
}
else {
return [tableView dequeueReusableCellWithIdentifier:PlaceholderCellIdentifier];
}
}
最后,让我们设置BOOL属性来跟踪数据是否正在加载,以及占位符是否隐藏。
4.设置isLoading
placeholderIsHidden
**:**
首先,将这两个声明添加到类的.h文件中:
@property (assign, nonatomic) BOOL isLoading;
@property (assign, nonatomic) BOOL placeholderIsHidden;
现在,在viewDidLoad
方法中设置它们的初始值:
self.isLoading = YES;
self.placeholderIsHidden = NO;
这就是placeholderIsHidden
属性!至于isLoading
属性,您需要在代码开始从服务器加载数据的任何地方将其设置为YES
,在该操作完成的任何地方设置为NO
。在我的例子中,它相当简单,因为当加载操作完成时,我使用了一个带有回调的操作。
就这样!运行您的代码,并看到整洁,流畅的动画和编辑安全的占位符单元格!
希望这对某些人有帮助!
编辑:更重要的一件事!重要的是要注意,你应该对你的代码做另外一件事,以避免任何讨厌的不必要的崩溃:检查你的代码,无论你在哪里访问数据源中的元素(通常使用objectAtIndex:
),确保你永远不会试图拉出一个不存在的元素。
在极少数情况下,这可能是一个问题,例如,您有访问数据源中所有可见行的元素的代码(由于占位符可能是可见的,因此您的indexPath可能与数据源中的任何元素都不相关)。(在我的例子中,这是一段特定的代码,一旦表格视图完成滚动,它就会在所有可见行上启动图像下载)。解决这个问题非常简单:无论您在何处访问数据源中的元素,都可以像这样在代码周围添加if语句:
int count = [self.dataSource count];
if (indexPath.row != count) {
// Code that accesses the element
// ie:
MyDataItem *dataItem = [self.dataSource objectAtIndex:indexPath.row];
}
祝好运!
发布于 2011-10-08 04:45:29
absolute实现这一点最简单的方法是实际拥有两个UITableViews
例如:
UITableView * mainTable;
UITableView * placeholderTable;
然后在你的委托中做一些事情,比如
tableView:(UITableView*)tableview cellForRowAtIndexPath:(NSIndexPath*)indexPath {
if (tableview == placeholderTable){
// code for rendering placeholder cell
}
else {
// code for rendering actual table content
}
}
无论何时插入/删除实体或初始化视图,都要检查是否为空,并隐藏/取消隐藏表:
// something changed the number of cells
if (tableIsEmpty){
mainTable.hidden = YES;
placeholderTable.hidden = NO;
}
else {
mainTable.hidden = NO;
placeholderTable.hidden = YES;
}
这样做将为您节省大量痛苦;尤其是在使用核心数据NSFetchedResultsController和表视图动画时。
https://stackoverflow.com/questions/4600948
复制相似问题