使用来自服务器的新数据更新单元格,而不重新加载相同的内容 - iOS
Updating cells with new data from server, without reloading content that is the same - iOS
我有一个从 webService 加载数据的 UITableView。我有一个跟踪我的数据的单例对象 (NSMutableArray *)
。加载新数据时,我向我的 tableViewController 发送通知并调用 [self.tableView reloadData];
这将重绘我的 table 并使用当前数据填充单元格。
我有两个问题。
如果我在重新加载数据时滚动,应用程序会崩溃,因为 (NSMutableArray *) 在加载新数据时有一秒钟是空的,我得到一个 index 4 beyond bounds for empty array
为了解决这个问题,我在调用 [self.tableView reloadData];
之前做了这个 [_myArray removeAllObjects];
,这修复了崩溃,但在再次绘制它们之前先清除所有单元格一秒钟。
这不是我想要达到的效果。我希望它像邮件应用程序一样重新加载单元格,它将在其中绘制和删除必要的单元格,并保留那些保持不变的单元格。
我也试过 reloadSections:withRowAnimation:UITableViewRowAnimationFade;
动画重新加载,但我仍然需要在重新加载之前清空数组。
我该怎么做?我觉得我需要创建一个数组的副本,然后将其移动过来,我在正确的道路上吗?这是我的重新加载功能。
- (void)reloadTable:(NSNotification *)notif {
[self.sellingTableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];
}
*****更新****
在@oren 的指导下,这是我想出的。这可行,但我担心 table 有大量数据,这不够有效。我试图编写一个哈希算法,该算法保留和索引以比较行以减少迭代次数,但结果集的顺序总是不同的问题。
我最终有 2 个列表。一个是我用来填充 table.. 的单例列表,而不是使用像 SQLite 3 的核心数据这样的本地数据存储,但是当你的应用程序关闭时,它们也可以用来保存数据。
当我唤醒我的应用程序或加载视图时。数据从我的单例列表中加载...然后我使用来自服务器的数据创建另一个数组,检查两个数组是否相互更改,然后 add/remove 必要的行。
-(void)sortRequestList:(NSNotification *)notification
{
RequestManager *newListManager = [[RequestManager alloc] init];
if ([notification.object isKindOfClass:[RequestManager class]])
{
newListManager = [notification object];//New Data that was downloaded from web
}
else
{
NSLog(@"Error, object not recognized.");
}
NSLog(@"newListManager count = %lu", (unsigned long)newListManager.requestNotFilledList.count);
NSLog(@"Before - requestNotFilledList count = %lu", (unsigned long)self.requestManager.requestNotFilledList.count);
NSMutableArray *insertIndexPaths = [[NSMutableArray alloc] init];
NSMutableArray *deleteIndexPaths = [[NSMutableArray alloc] init];
if ([self.requestManager.requestNotFilledList count] == 0)
{
[newListManager.requestNotFilledList enumerateObjectsUsingBlock:^(Request *request, NSUInteger index, BOOL *stop) {
[self.requestManager.requestNotFilledList insertObject:request atIndex:index];
[insertIndexPaths addObject:[NSIndexPath indexPathForRow:index inSection:0]];
}];
} else {
__block BOOL foundMatch;
[self.requestManager.requestNotFilledList enumerateObjectsUsingBlock:^(Request *oldReq, NSUInteger index, BOOL *stop) {
foundMatch = false;
[newListManager.requestNotFilledList enumerateObjectsUsingBlock:^(Request *newReq, NSUInteger newIdx, BOOL *stop) {
if ([oldReq.requestID isEqualToString:newReq.requestID])
{
foundMatch = true;
*stop = YES;
}
}];
if (foundMatch == false)
{
[deleteIndexPaths addObject:[NSIndexPath indexPathForRow:index inSection:0]];
[self.requestManager.requestNotFilledList removeObjectAtIndex:index];
}
}];
[newListManager.requestNotFilledList enumerateObjectsUsingBlock:^(Request *newReq, NSUInteger index, BOOL *stop) {
foundMatch = false;
[self.requestManager.requestNotFilledList enumerateObjectsUsingBlock:^(Request *oldReq, NSUInteger oldIdx, BOOL *stop) {
if ([oldReq.requestID isEqualToString:newReq.requestID])
{
foundMatch = true;
*stop = YES;
}
}];
if (foundMatch == false)
{
[insertIndexPaths addObject:[NSIndexPath indexPathForRow:index inSection:0]];
[self.requestManager.requestNotFilledList insertObject:newReq atIndex:index];
}
}];
}
NSLog(@"After - requestNotFilledList count = %lu", (unsigned long)self.requestManager.requestNotFilledList.count);
UITableView *tv = (UITableView *)self.view;
[tv beginUpdates];
[tv insertRowsAtIndexPaths:insertIndexPaths withRowAnimation:UITableViewRowAnimationRight];
[tv deleteRowsAtIndexPaths:deleteIndexPaths withRowAnimation:UITableViewRowAnimationFade];
[tv endUpdates];
[self.refreshControl endRefreshing];
}
如何插入新的 indexPaths 并用动画删除不需要的 indexPaths:
- insertRowsAtIndexPaths:withRowAnimation:
- deleteRowsAtIndexPaths:withRowAnimation:
您需要有一个要删除的 indexPaths 数组,假设您有一个数组 [1,2,3,6] 并删除 2 和 6,您需要有第 1 行和第 3 行的数组 indexPaths .
此外,您需要有一个要插入的索引路径数组:假设您像这样添加 a,b,c:[a,1,3,b,c,7],所以 indexPaths 将用于行 - 0,3,4.
我写了一个简单的代码作为示例:我的tableView中的起始数据是[1,2,3,4,5,6],在下面的代码中我将其更新为[1, new1, 2, new2, 3, 5, new3, 7]。棘手的部分是计算用于插入和删除的 indextPaths。
- (void)updateTableView
{
[self.tableView beginUpdates];
// First remove the objects
[self.dataArray removeObjectAtIndex:5];
[self.dataArray removeObjectAtIndex:3];
NSArray *indexPathsToRemove = [NSArray arrayWithObjects:
[NSIndexPath indexPathForRow:3 inSection:0],
[NSIndexPath indexPathForRow:5 inSection:0], nil];
// Insert the new ones
[self.dataArray insertObject:@"New data 3" atIndex:4];
[self.dataArray insertObject:@"New data 2" atIndex:2];
[self.dataArray insertObject:@"New data 1" atIndex:1];
NSArray *indexPathsToInsert = [NSArray arrayWithObjects:
[NSIndexPath indexPathForRow:1 inSection:0],
[NSIndexPath indexPathForRow:3 inSection:0],
[NSIndexPath indexPathForRow:6 inSection:0], nil];
// Animate the insertion and deletion
[self.tableView deleteRowsAtIndexPaths:indexPathsToRemove withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView insertRowsAtIndexPaths:indexPathsToInsert withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];
}
您始终需要在插入之前考虑删除,在 'beginUpdats' 的动画块中,tableView 在处理完所有删除之前不会插入新行或部分。
我有一个从 webService 加载数据的 UITableView。我有一个跟踪我的数据的单例对象 (NSMutableArray *)
。加载新数据时,我向我的 tableViewController 发送通知并调用 [self.tableView reloadData];
这将重绘我的 table 并使用当前数据填充单元格。
我有两个问题。
如果我在重新加载数据时滚动,应用程序会崩溃,因为 (NSMutableArray *) 在加载新数据时有一秒钟是空的,我得到一个
index 4 beyond bounds for empty array
为了解决这个问题,我在调用
[self.tableView reloadData];
之前做了这个[_myArray removeAllObjects];
,这修复了崩溃,但在再次绘制它们之前先清除所有单元格一秒钟。
这不是我想要达到的效果。我希望它像邮件应用程序一样重新加载单元格,它将在其中绘制和删除必要的单元格,并保留那些保持不变的单元格。
我也试过 reloadSections:withRowAnimation:UITableViewRowAnimationFade;
动画重新加载,但我仍然需要在重新加载之前清空数组。
我该怎么做?我觉得我需要创建一个数组的副本,然后将其移动过来,我在正确的道路上吗?这是我的重新加载功能。
- (void)reloadTable:(NSNotification *)notif {
[self.sellingTableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];
}
*****更新****
在@oren 的指导下,这是我想出的。这可行,但我担心 table 有大量数据,这不够有效。我试图编写一个哈希算法,该算法保留和索引以比较行以减少迭代次数,但结果集的顺序总是不同的问题。
我最终有 2 个列表。一个是我用来填充 table.. 的单例列表,而不是使用像 SQLite 3 的核心数据这样的本地数据存储,但是当你的应用程序关闭时,它们也可以用来保存数据。
当我唤醒我的应用程序或加载视图时。数据从我的单例列表中加载...然后我使用来自服务器的数据创建另一个数组,检查两个数组是否相互更改,然后 add/remove 必要的行。
-(void)sortRequestList:(NSNotification *)notification
{
RequestManager *newListManager = [[RequestManager alloc] init];
if ([notification.object isKindOfClass:[RequestManager class]])
{
newListManager = [notification object];//New Data that was downloaded from web
}
else
{
NSLog(@"Error, object not recognized.");
}
NSLog(@"newListManager count = %lu", (unsigned long)newListManager.requestNotFilledList.count);
NSLog(@"Before - requestNotFilledList count = %lu", (unsigned long)self.requestManager.requestNotFilledList.count);
NSMutableArray *insertIndexPaths = [[NSMutableArray alloc] init];
NSMutableArray *deleteIndexPaths = [[NSMutableArray alloc] init];
if ([self.requestManager.requestNotFilledList count] == 0)
{
[newListManager.requestNotFilledList enumerateObjectsUsingBlock:^(Request *request, NSUInteger index, BOOL *stop) {
[self.requestManager.requestNotFilledList insertObject:request atIndex:index];
[insertIndexPaths addObject:[NSIndexPath indexPathForRow:index inSection:0]];
}];
} else {
__block BOOL foundMatch;
[self.requestManager.requestNotFilledList enumerateObjectsUsingBlock:^(Request *oldReq, NSUInteger index, BOOL *stop) {
foundMatch = false;
[newListManager.requestNotFilledList enumerateObjectsUsingBlock:^(Request *newReq, NSUInteger newIdx, BOOL *stop) {
if ([oldReq.requestID isEqualToString:newReq.requestID])
{
foundMatch = true;
*stop = YES;
}
}];
if (foundMatch == false)
{
[deleteIndexPaths addObject:[NSIndexPath indexPathForRow:index inSection:0]];
[self.requestManager.requestNotFilledList removeObjectAtIndex:index];
}
}];
[newListManager.requestNotFilledList enumerateObjectsUsingBlock:^(Request *newReq, NSUInteger index, BOOL *stop) {
foundMatch = false;
[self.requestManager.requestNotFilledList enumerateObjectsUsingBlock:^(Request *oldReq, NSUInteger oldIdx, BOOL *stop) {
if ([oldReq.requestID isEqualToString:newReq.requestID])
{
foundMatch = true;
*stop = YES;
}
}];
if (foundMatch == false)
{
[insertIndexPaths addObject:[NSIndexPath indexPathForRow:index inSection:0]];
[self.requestManager.requestNotFilledList insertObject:newReq atIndex:index];
}
}];
}
NSLog(@"After - requestNotFilledList count = %lu", (unsigned long)self.requestManager.requestNotFilledList.count);
UITableView *tv = (UITableView *)self.view;
[tv beginUpdates];
[tv insertRowsAtIndexPaths:insertIndexPaths withRowAnimation:UITableViewRowAnimationRight];
[tv deleteRowsAtIndexPaths:deleteIndexPaths withRowAnimation:UITableViewRowAnimationFade];
[tv endUpdates];
[self.refreshControl endRefreshing];
}
如何插入新的 indexPaths 并用动画删除不需要的 indexPaths:
- insertRowsAtIndexPaths:withRowAnimation:
- deleteRowsAtIndexPaths:withRowAnimation:
您需要有一个要删除的 indexPaths 数组,假设您有一个数组 [1,2,3,6] 并删除 2 和 6,您需要有第 1 行和第 3 行的数组 indexPaths .
此外,您需要有一个要插入的索引路径数组:假设您像这样添加 a,b,c:[a,1,3,b,c,7],所以 indexPaths 将用于行 - 0,3,4.
我写了一个简单的代码作为示例:我的tableView中的起始数据是[1,2,3,4,5,6],在下面的代码中我将其更新为[1, new1, 2, new2, 3, 5, new3, 7]。棘手的部分是计算用于插入和删除的 indextPaths。
- (void)updateTableView
{
[self.tableView beginUpdates];
// First remove the objects
[self.dataArray removeObjectAtIndex:5];
[self.dataArray removeObjectAtIndex:3];
NSArray *indexPathsToRemove = [NSArray arrayWithObjects:
[NSIndexPath indexPathForRow:3 inSection:0],
[NSIndexPath indexPathForRow:5 inSection:0], nil];
// Insert the new ones
[self.dataArray insertObject:@"New data 3" atIndex:4];
[self.dataArray insertObject:@"New data 2" atIndex:2];
[self.dataArray insertObject:@"New data 1" atIndex:1];
NSArray *indexPathsToInsert = [NSArray arrayWithObjects:
[NSIndexPath indexPathForRow:1 inSection:0],
[NSIndexPath indexPathForRow:3 inSection:0],
[NSIndexPath indexPathForRow:6 inSection:0], nil];
// Animate the insertion and deletion
[self.tableView deleteRowsAtIndexPaths:indexPathsToRemove withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView insertRowsAtIndexPaths:indexPathsToInsert withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];
}
您始终需要在插入之前考虑删除,在 'beginUpdats' 的动画块中,tableView 在处理完所有删除之前不会插入新行或部分。