使用来自服务器的新数据更新单元格,而不重新加载相同的内容 - iOS

Updating cells with new data from server, without reloading content that is the same - iOS

我有一个从 webService 加载数据的 UITableView。我有一个跟踪我的数据的单例对象 (NSMutableArray *)。加载新数据时,我向我的 tableViewController 发送通知并调用 [self.tableView reloadData]; 这将重绘我的 table 并使用当前数据填充单元格。

我有两个问题。

  1. 如果我在重新加载数据时滚动,应用程序会崩溃,因为 (NSMutableArray *) 在加载新数据时有一秒钟是空的,我得到一个 index 4 beyond bounds for empty array

  2. 为了解决这个问题,我在调用 [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 在处理完所有删除之前不会插入新行或部分。