UITableView动态单元格大小调整和异步加载图像导致奇怪的滚动行为

时间:2015-06-25 06:24:30

标签: ios objective-c uitableview core-data nsfetchedresultscontroller

我有一个UITableView + NSFetchedResultsController组合用于加载评论。评论包括姓名,日期,正文,重要的是头像图像。评论存储在核心数据中。

在viewDidLoad上,它会检查是否已从我的源加载了注释。如果尚未下载,则会生成NSOperation以下载并抓取HTML以获取每个评论的头像的名称,日期,正文和URL。它还为所有这些创建了核心数据实体。作为后台线程NSOperation,它使用私有NSManagedObjectContext来完成工作并将其保存回主MOC。

将这些添加到商店后,我的NSFetchedResultsController调用委托方法controllerWillChangeContent:,didChangeSection:,didChangeObject:和controllerDidChangeContent:。插入一批新注释时它可以正常工作,并将它们插入到表视图中就可以了。

我的表格视图单元格使用iOS8的UITableViewAutomaticDimension来获得动态高度,具体取决于每条评论的内容长度。如果我只显示每个评论的文本(暂时忽略头像图像),一切都按预期工作,滚动浏览评论工作正常,这看起来很普通。

然而,当它加载头像图像时我的问题就出现了。在我的configureCell :(由cellForRowAtIndexPath:和controller:didChangeObject :)调用,它检查图像是否已经下载。如果没有,它会产生一种新的,不同类型的NSOperation,以根据评论的avatarImageURL属性获取图像。获取后,它会将图像数据添加到Core Data实体中。它还通过自定义委派方法向表视图控制器发出信号,表明映像已更新。我的获取结果控制器还通过控制器发出我的表视图控制器视图:didChangeObject:。

这就是问题所在。

当滚动速度非常快时,这个数据第一次被下载并存储到Core Data中,表视图和动态调整大小的单元格非常奇怪。当向上和向下滚动时,它们会多次改变大小。只有在滚动整个单元格一次或两次之后,一切都很顺利。

当我完全禁用NSOperations的产生以下载头像时,一切都可以正常工作并且滚动得很好。但是,如果我让它下载图像并将它们添加到Core Data实体,但是无法实现自定义委托方法和控制器的NSFetchedResultsChangeUpdate情况:didChangeObject:,它仍然会导致最初的奇怪,不连贯的滚动。

我通过NSLogs注意到的一件事是,所有注释都在一个批处理中插入到表视图中,而每次下载单个头像时,获取的结果控制器将发出willChangeContent:,didChangeObject:和didChangeContent :。同样,这三种方法都适用于单个化身。

相关方法(如有必要,我可以添加更多):

- (void)viewDidLoad {
    [super viewDidLoad];

    // First get our current array of comic infos, initiating the controller
    NSError *fetchError;
    if (![[self fetchedResultsController] performFetch:&fetchError]) {
        NSLog(@"Error in the fetched results controller: %@", fetchError.description);
        return; // Don't do anything else.
    }

    self.tableView.estimatedRowHeight = 70.0f;
    self.tableView.rowHeight = UITableViewAutomaticDimension;

    if (self.comicInfo.comments == nil || self.comicInfo.comments.count == 0) {
        CommentsRetrieverOperation *operation = [[CommentsRetrieverOperation alloc] initWithComicInfoID:self.comicInfo.objectID];
        operation.managedObjectContext = self.managedObjectContext;
        [self.commentDownloadQueue addOperation:operation];
    }
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CommentCell" forIndexPath:indexPath];

    // Configure the cell...
    [self configureCell:cell atIndexPath:indexPath];

    return cell;
}

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
    CommentTableViewCell *commentCell = (CommentTableViewCell *)cell;

    Comment *comment = [self.fetchedResultsController objectAtIndexPath:indexPath];

    commentCell.nameLabel.text = comment.name;
    commentCell.dateLabel.text = comment.date;
    commentCell.commentLabel.text = comment.content;
    commentCell.commentLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];

    if ([comment isAdmin].boolValue == true) {
        commentCell.contentView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"BackgroundPattern1"]];
    } else if (indexPath.row % 2 == 0) {
        commentCell.contentView.backgroundColor = [UIColor colorWithRed:248.0f/255.0f green:248.0f/255.0f blue:248.0f/255.0f alpha:1.0f];
    } else {
        commentCell.contentView.backgroundColor = [UIColor whiteColor];
    }

    if (comment.avatarImage == nil || comment.avatarImage.image == nil) {
        CommentImageDownloadOperation *operation = [[CommentImageDownloadOperation alloc] initWithCommentID:comment.objectID];
        operation.managedObjectContext = self.managedObjectContext;
        operation.delegate = self;
        operation.userInfo = @{ @"cell":commentCell, @"indexPath":indexPath };
        [self.avatarDownloadQueue addOperation:operation];
    } else {
        [self setAvatarImageForCell:commentCell withComment:comment];
    }

    commentCell.userInteractionEnabled = NO;
}

- (void)setAvatarImageForCell:(CommentTableViewCell *)cell withComment:(Comment *)comment {
    if (comment.avatarImage.isDefaultImage.boolValue) {
        cell.avatarImage = self.defaultAvatarImage;
    } else {
        UIImage *image = [UIImage imageWithData:comment.avatarImage.image];
        cell.avatarImage = image;
    }
}

- (void)commentImageDownloadOperation:(CommentImageDownloadOperation *)operation didFinishDownloadWithUserInfo:(id)userInfo {
    NSIndexPath *indexPath = [self.tableView indexPathForCell:userInfo[@"cell"]];
    if (indexPath == userInfo[@"indexPath"]) {
        CommentTableViewCell *cell = userInfo[@"cell"];
        Comment *tempComment = (Comment *)[self.managedObjectContext objectWithID:operation.commentID];
        [self setAvatarImageForCell:cell withComment:tempComment];
    }
}

#pragma mark - Fetched Results Controller Delegate
All of these methods are copied exactly from Apple's sample at https://developer.apple.com/library/ios/documentation/CoreData/Reference/NSFetchedResultsControllerDelegate_Protocol/index.html#//apple_ref/doc/uid/TP40008228-CH1-SW10

所以很快,我的问题是,当在背景中为动态大小的单元格加载图像时,以及使用Core Data和获取的结果控制器时,滚动非常不连贯。有没有更好的方法来实现这一切,我是否实现了这个错误?有没有比一开始加载所有图像更好的方法?

编辑:一些澄清。当我在表视图中向下滚动时,获取的结果控制器会向对象发送更新。因此,当我向下滚动willChangeContent:时,didChangeObject:for type = NSFetchedResultsChangeUpdate,并为进入视图的每个新单元调用didChangeContent:。

0 个答案:

没有答案