为了账号安全,请及时绑定邮箱和手机立即绑定

使用GCD异步下载UITableView的图像

使用GCD异步下载UITableView的图像

iOS
MMTTMM 2019-10-08 14:51:33
我正在使用GCD异步下载uitableview的图像,但是有一个问题-滚动图像闪烁并一直更改时。我尝试将每个单元格的图像设置为nil,但这并没有太大帮助。快速向上滚动时,所有图像均不正确。我该怎么办?这是我的细胞方法:- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{    static NSString *CellIdentifier = @"Cell";    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];    if (self.loader.parsedData[indexPath.row] != nil)    {        cell.imageView.image = nil;        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);            dispatch_async(queue, ^(void) {                NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[self.loader.parsedData[indexPath.row] objectForKey:@"imageLR"]]];                UIImage* image = [[UIImage alloc] initWithData:imageData];                dispatch_async(dispatch_get_main_queue(), ^{                    cell.imageView.image = image;                    [cell setNeedsLayout];                     });            });    cell.textLabel.text = [self.loader.parsedData[indexPath.row] objectForKey:@"id"];    }    return cell;}
查看完整描述

3 回答

?
GCT1015

TA贡献1827条经验 获得超4个赞

这里的问题是您的图像获取块正在保存对tableview单元的引用。下载完成后,imageView.image即使您回收了单元格以显示其他行,它也会设置属性。


在设置映像之前,您需要下载完成块来测试映像是否仍与单元格相关。


还需要注意的是,您不会将图像存储在单元格中以外的任何位置,因此,每次在屏幕上滚动一行时,您都将再次下载它们。您可能希望将它们缓存在某个位置,并在开始下载之前查找本地缓存的图像。


编辑:这是使用单元格的tag属性进行测试的简单方法:


- (UITableViewCell *)tableView:(UITableView *)tableView 

         cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];


    cell.tag = indexPath.row;

    NSDictionary *parsedData = self.loader.parsedData[indexPath.row];

    if (parsedData)

    {

        cell.imageView.image = nil;

        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);

        dispatch_async(queue, ^(void) {


            NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:parsedData[@"imageLR"]];


            UIImage* image = [[UIImage alloc] initWithData:imageData];

            if (image) {

                 dispatch_async(dispatch_get_main_queue(), ^{

                     if (cell.tag == indexPath.row) {

                         cell.imageView.image = image;

                         [cell setNeedsLayout];

                     }

                 });

             }

        });


        cell.textLabel.text = parsedData[@"id"];

    }

    return cell;

}


查看完整回答
反对 回复 2019-10-08
?
ibeautiful

TA贡献1993条经验 获得超5个赞

关键是您没有完全了解单元重用概念。这与异步下载不太吻合。



    ^{

    cell.imageView.image = image;

    [cell setNeedsLayout];

}

在请求完成并且所有数据都已加载时被执行。但是,在创建块时,单元会获得其值。


到执行块时,单元仍指向现有单元之一。但是用户很有可能继续滚动。同时,该单元格对象已被重新使用,并且该图像与“ 旧 ”单元格相关联,该单元格已被重用,分配和显示。之后不久,除非用户进一步滚动,否则将加载并分配并显示正确的图像。等等等等。


您应该寻找一种更聪明的方法。有很多花絮。Google用于延迟加载图片。


查看完整回答
反对 回复 2019-10-08
?
慕侠2389804

TA贡献1719条经验 获得超6个赞

使用索引路径获取单元格。如果看不到该单元格nil,则不会有问题。当然,您可能希望在下载数据时缓存数据,以便在已有图像后立即设置单元格的图像。


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];


    if (self.loader.parsedData[indexPath.row] != nil)

    {

        cell.imageView.image = nil;

        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);

            dispatch_async(queue, ^(void) {

                //  You may want to cache this explicitly instead of reloading every time.

                NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[self.loader.parsedData[indexPath.row] objectForKey:@"imageLR"]]];

                UIImage* image = [[UIImage alloc] initWithData:imageData];

                dispatch_async(dispatch_get_main_queue(), ^{

                    // Capture the indexPath variable, not the cell variable, and use that

                    UITableViewCell *blockCell = [tableView cellForRowAtIndexPath:indexPath];

                    blockCell.imageView.image = image;

                    [blockCell setNeedsLayout];

                });

            });

        cell.textLabel.text = [self.loader.parsedData[indexPath.row] objectForKey:@"id"];

    }


    return cell;

}


查看完整回答
反对 回复 2019-10-08
  • 3 回答
  • 0 关注
  • 522 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信