Kex*_*Kex 17 objective-c uitableview ios
撕掉我的头发试图让它发挥作用.我想表演[self.tableView deleteRowsAtIndexPaths:indexes withRowAnimation:UITableViewRowAnimationLeft];,
我如何删除更详细的代码:
int index = (int)[self.messages indexOfObject:self.messageToDelete];
[self.messages removeObject:self.messageToDelete];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
NSArray *indexes = [[NSArray alloc] initWithObjects:indexPath, nil];
[self.tableView deleteRowsAtIndexPaths:indexes withRowAnimation:UITableViewRowAnimationLeft];
Run Code Online (Sandbox Code Playgroud)
这工作正常但是如果我收到推送通知(即收到新消息),删除应用程序将崩溃并显示如下错误:
断言失败 - [UITableView _endCellAnimationsWithContext:],/ SourceCache/UIKit/UIKit-3347.44/UITableView.m:1327 2015-07-04 19:12:48.623 myapp [319:24083]***由于未捕获的异常而终止应用程序' NSInternalInconsistencyException',原因:'尝试从更新前仅包含1行的第0部分删除第1行'
我怀疑这是因为我的数据源正在改变,数组的大小
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
Run Code Online (Sandbox Code Playgroud)
删除时的引用将不一致,因为当推送通知触发刷新时它会增加1.有什么方法可以解决这个问题吗?我是否正确deleteRowsAtIndexPaths使用该numberOfRowsInSection方法?
因此,为了解决您的问题,您需要确保在放置一些表格视图动画时数据源不会更改。我建议执行以下操作。
首先,创建两个数组:messagesToDelete和messagesToInsert。这些将保存有关您要删除/插入哪些消息的信息。
其次,将布尔属性添加updatingTable到表视图数据源。
第三,添加以下功能:
-(void)updateTableIfPossible {
if (!updatingTable) {
updatingTable = [self updateTableViewWithNewUpdates];
}
}
-(BOOL)updateTableViewWithNewUpdates {
if ((messagesToDelete.count == 0)&&(messagesToInsert.count==0)) {
return false;
}
NSMutableArray *indexPathsForMessagesThatNeedDelete = [[NSMutableArray alloc] init];
NSMutableArray *indexPathsForMessagesThatNeedInsert = [[NSMutableArray alloc] init];
// for deletion you need to use original messages to ensure
// that you get correct index paths if there are multiple rows to delete
NSMutableArray *oldMessages = [self.messages copy];
for (id message in messagesToDelete) {
int index = (int)[self.oldMessages indexOfObject:message];
[self.messages removeObject:message];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
[indexPathsForMessagesThatNeedDelete addObject:indexPath];
}
for (id message in messagesToInsert) {
[self.messages insertObject:message atIndex:0];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[indexPathsForMessagesThatNeedInsert addObject:indexPath];
}
[messagesToDelete removeAllObjects];
[messagesToInsert removeAllObjects];
// at this point your messages array contains
// all messages which should be displayed at CURRENT time
// now do the following
[CATransaction begin];
[CATransaction setCompletionBlock:^{
updatingTable = NO;
[self updateTableIfPossible];
}];
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:indexPathsForMessagesThatNeedDelete withRowAnimation:UITableViewRowAnimationLeft];
[tableView insertRowsAtIndexPaths:indexPathsForMessagesThatNeedInsert withRowAnimation:UITableViewRowAnimationLeft];
[tableView endUpdates];
[CATransaction commit];
return true;
}
Run Code Online (Sandbox Code Playgroud)
最后,您需要在要添加/删除行的所有函数中使用以下代码。
添加信息
[self.messagesToInsert addObject:message];
[self updateTableIfPossible];
Run Code Online (Sandbox Code Playgroud)
删除信息
[self.messagesToDelete addObject:message];
[self updateTableIfPossible];
Run Code Online (Sandbox Code Playgroud)
该代码的作用是确保数据源的稳定性。只要有更改,就将需要插入/删除的消息添加到数组(messagesToDelete和messagesToDelete)中。然后,您调用一个函数updateTableIfPossible,该函数将更新表视图的数据源(并为更改添加动画效果),前提是当前没有正在进行的动画。如果正在进行动画制作,则在此阶段将不执行任何操作。
但是,因为我们添加了完成
[CATransaction setCompletionBlock:^{
updatingTable = NO;
[self updateTableIfPossible];
}];
Run Code Online (Sandbox Code Playgroud)
在动画结束时,我们的数据源将检查是否需要将任何新更改应用于表格视图,如果有,则将更新动画。
这是更新数据源的更安全的方法。请让我知道它是否适合您。
我对上面的代码进行了一些测试。你不能同时做到这一点,我们至少可以做的是:
//This will wait for `deleteRowsAtIndexPaths:indexes` before the `setCompletionBlock `
//
[CATransaction begin];
[CATransaction setCompletionBlock:^{
[tableView reloadData];
}];
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:indexes withRowAnimation:UITableViewRowAnimationLeft];
[tableView endUpdates];
[CATransaction commit];
Run Code Online (Sandbox Code Playgroud)
在你的代码中有:
/*
int index = (int)[self.messages indexOfObject:self.messageToDelete];
[self.messages removeObject:self.messageToDelete];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
NSArray *indexes = [[NSArray alloc] initWithObjects:indexPath, nil];
[self.tableView deleteRowsAtIndexPaths:indexes withRowAnimation:UITableViewRowAnimationLeft];
*/
Run Code Online (Sandbox Code Playgroud)
这是不安全的,因为如果触发通知并且由于某种原因在[self.tableView reloadData];通知之前调用deleteRowsAtIndexPaths将导致崩溃,因为 tableView 当前正在更新数据然后被 中断deleteRowsAtIndexPaths:,请尝试按以下顺序进行检查:
/*
...
[self.tableView reloadData];
[self.tableView deleteRowsAtIndexPaths:indexes withRowAnimation:UITableViewRowAnimationLeft];
This will cause a crash...
*/
Run Code Online (Sandbox Code Playgroud)
嗯.. 回到你的代码,让我们进行一个可能导致崩溃的模拟。这只是一个假设,所以这并不是 100% 确定 (99%)。:)
假设self.messageToDelete等于 nil;
int index = (int)[self.messages indexOfObject: nil];
// since you casted this to (int) with would be 0, so `index = 0`
[self.messages removeObject: nil];
// self.messages will remove nil object which is non existing therefore
// self.messages is not updated/altered/changed
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
// indexPath.row == 0
NSArray *indexes = [[NSArray alloc] initWithObjects:indexPath, nil];
[self.tableView deleteRowsAtIndexPaths:indexes withRowAnimation:UITableViewRowAnimationLeft];
// then you attempted to delete index indexPath at row 0 in section 0
// but the datasource is not updated meaning this will crash..
//
// Same thing will happen if `self.messageToDelete` is not existing in your `self.messages `
Run Code Online (Sandbox Code Playgroud)
我的建议是检查第self.messageToDelete一个:
if ([self.messages containsObject:self.messageToDelete])
{
// your delete code...
}
Run Code Online (Sandbox Code Playgroud)
希望这对您有所帮助,干杯!;)
| 归档时间: |
|
| 查看次数: |
2377 次 |
| 最近记录: |