【问题标题】:Async image loading from url inside a UITableView cell - image changes to wrong image while scrolling从 UITableView 单元格内的 url 异步加载图像 - 滚动时图像更改为错误图像
【发布时间】:2013-05-15 20:14:20
【问题描述】:

我已经编写了两种方法来异步加载我的 UITableView 单元格中的图片。在这两种情况下,图像都可以正常加载,但是当我滚动表格时,图像会更改几次,直到滚动结束并且图像将返回到正确的图像。我不知道为什么会这样。

#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

- (void)viewDidLoad
{
    [super viewDidLoad];
    dispatch_async(kBgQueue, ^{
        NSData* data = [NSData dataWithContentsOfURL: [NSURL URLWithString:
                                                       @"http://myurl.com/getMovies.php"]];
        [self performSelectorOnMainThread:@selector(fetchedData:)
                               withObject:data waitUntilDone:YES];
    });
}

-(void)fetchedData:(NSData *)data
{
    NSError* error;
    myJson = [NSJSONSerialization
              JSONObjectWithData:data
              options:kNilOptions
              error:&error];
    [_myTableView reloadData];
}    

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    // Return the number of rows in the section.
    // Usually the number of items in your array (the one that holds your list)
    NSLog(@"myJson count: %d",[myJson count]);
    return [myJson count];
}
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

        myCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
        if (cell == nil) {
            cell = [[myCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
        }

        dispatch_async(kBgQueue, ^{
        NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://myurl.com/%@.jpg",[[myJson objectAtIndex:indexPath.row] objectForKey:@"movieId"]]]];

            dispatch_async(dispatch_get_main_queue(), ^{
        cell.poster.image = [UIImage imageWithData:imgData];
            });
        });
         return cell;
}

... ...

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

            myCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
            if (cell == nil) {
                cell = [[myCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
            }
    NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:@"http://myurl.com/%@.jpg",[[myJson objectAtIndex:indexPath.row] objectForKey:@"movieId"]]];
    NSURLRequest* request = [NSURLRequest requestWithURL:url];


    [NSURLConnection sendAsynchronousRequest:request
                                       queue:[NSOperationQueue mainQueue]
                           completionHandler:^(NSURLResponse * response,
                                               NSData * data,
                                               NSError * error) {
                               if (!error){
                                   cell.poster.image = [UIImage imageWithData:data];
                                   // do whatever you want with image
                               }

                           }];
     return cell;
}

【问题讨论】:

  • 您正在尝试将信息存储在实际单元格中。这很糟糕,非常糟糕。您应该将信息存储在 n 数组(或类似的东西)中,然后将其显示在单元格中。本例中的信息是实际的 UIImage。是的,异步加载它,但将它加载到一个数组中。
  • @Fogmeister 你指的是poster吗?这大概是他的自定义单元格中的一个图像视图,所以 EXEC_BAD_ACCESS 正在做的事情是完全正确的。您不应该将单元格用作模型数据的存储库是正确的,但我认为这不是他正在做的事情。他只是为自定义单元格提供了它需要展示自己的东西。此外,这是一个更微妙的问题,我会警惕将图像本身存储在支持 tableview 的模型数组中。最好使用图像缓存机制,并且您的模型对象应该从该缓存中检索。
  • 是的,正是我的观点。查看请求(已完整显示),他正在异步下载图像并将其直接放入单元格中的 imageView 中。 (因此使用单元格来存储数据,即图像)。他应该做的是引用一个对象并从该对象请求图像(包含在数组或某处)。如果对象还没有图像,它应该返回一个占位符并下载图像。然后,当图像下载并准备好显示时,让表格知道,以便它可以更新单元格(如果它是可见的)。
  • 每次他滚动到表格中的那个单元格时,他所做的都会强制下载。图像是否持久存储取决于他,但至少要在 tableview 的生命周期内存储它们。
  • 正是 :D 这样您只需要从 URL 中获取图像一次。你会在 Facebook Friend Picker 之类的东西上看到这一点。当您启动它时,所有头像都是灰色的占位符。然后当你滚动它们时,它们都会随着它的移动而填充。但是,当您滚动回之前显示的单元格时,它会立即显示已下载的图像。

标签: ios objective-c cocoa-touch uitableview


【解决方案1】:

假设您正在寻找快速的战术修复,您需要做的是确保单元格图像已初始化并且单元格的行仍然可见,例如:

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

    cell.poster.image = nil; // or cell.poster.image = [UIImage imageNamed:@"placeholder.png"];

    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://myurl.com/%@.jpg", self.myJson[indexPath.row][@"movieId"]]];

    NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (data) {
            UIImage *image = [UIImage imageWithData:data];
            if (image) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    MyCell *updateCell = (id)[tableView cellForRowAtIndexPath:indexPath];
                    if (updateCell)
                        updateCell.poster.image = image;
                });
            }
        }
    }];
    [task resume];

    return cell;
}

上面的代码解决了由于单元被重用的事实而产生的一些问题:

  1. 在启动后台请求之前,您没有初始化单元格图像(这意味着在下载新图像时,出列单元格的最后一张图像仍然可见)。确保nil 任何图像视图的image 属性,否则您会看到图像闪烁。

  2. 一个更微妙的问题是,在一个非常慢的网络上,您的异步请求可能不会在单元格滚出屏幕之前完成。您可以使用UITableView 方法cellForRowAtIndexPath:(不要与名称相似的UITableViewDataSource 方法tableView:cellForRowAtIndexPath: 混淆)来查看该行的单元格是否仍然可见。如果单元格不可见,此方法将返回 nil

    问题在于,当您的异步方法完成时,单元格已经滚动,更糟糕的是,该单元格已被重新用于表格的另一行。通过检查该行是否仍然可见,您可以确保不会意外使用已滚动离开屏幕的行的图像来更新图像。

  3. 与手头的问题有点无关,我仍然觉得有必要更新它以利用现代约定和 API,特别是:

    • 使用NSURLSession 而不是将-[NSData contentsOfURL:] 分派到后台队列;

    • 使用dequeueReusableCellWithIdentifier:forIndexPath: 而不是dequeueReusableCellWithIdentifier:(但请确保使用单元原型或注册类或NIB 作为该标识符);和

    • 我使用了符合Cocoa naming conventions(即以大写字母开头)的类名。

即使进行了这些更正,也存在一些问题:

  1. 上面的代码没有缓存下载的图片。这意味着如果您在屏幕上滚动图像并返回屏幕,应用程序可能会尝试再次检索图像。也许您会很幸运,您的服务器响应标头将允许 NSURLSessionNSURLCache 提供的相当透明的缓存,但如果不是这样,您将发出不必要的服务器请求并提供更慢的用户体验。

  2. 我们不会取消对滚动到屏幕外的单元格的请求。因此,如果您快速滚动到第 100 行,则该行的图像可能会积压在对前 99 行的请求之后,这些请求甚至不再可见。您总是希望确保优先考虑对可见单元格的请求以获得最佳用户体验。

解决这些问题的最简单方法是使用UIImageView 类别,例如SDWebImageAFNetworking 提供的类别。如果您愿意,您可以编写自己的代码来处理上述问题,但工作量很大,而上述UIImageView 类别已经为您完成了这项工作。

【讨论】:

  • 谢谢。我相信你需要编辑你的答案。 updateCell.poster.image = nilcell.poster.image = nil; updateCell 在声明之前被调用。
  • 我的应用程序使用了大量的 json,所以AFNetworking 绝对是要走的路。我知道它,但懒得使用它。我只是在欣赏缓存如何与他们简单的代码行一起工作。 [imageView setImageWithURL:<#(NSURL *)#> placeholderImage:<#(UIImage *)#>];
  • 尝试了以上所有方法和 SDWebImage(实际上停在这里,甚至不需要尝试 AFNetworking),这是迄今为止最好的选择。谢谢@Rob。
  • 当您完成加载图像并更新图像视图时,移除活动指示器。唯一的技巧是,如果在检索图像时单元格滚动出视图并且单元格被重新用于另一行,您必须预测会发生什么情况,您必须检测任何现有活动指示器的存在并删除/更新它,而不仅仅是假设单元格没有现有指标。
  • 最初的问题是“为什么这个cellForRowAtIndexPath 在我快速滚动时会导致图像闪烁”,我解释了为什么会发生这种情况以及如何解决它。但是我继续解释了为什么即使这样还不够,描述了一些更深层次的问题,并争论了为什么最好使用其中一个库来更优雅地处理这个问题(优先考虑对可见单元的请求,缓存以避免冗余网络请求等)。对于“如何停止在我的表格视图中闪烁的图像”的问题,我不清楚您还期望什么。
【解决方案2】:

/*我是这样弄的,也测试过*/

第 1 步 = 在 vi​​ewDidLoad 方法中为类似这样的表格注册自定义单元格类(如果表格中的原型单元格)或 nib(如果自定义单元格的自定义笔尖):

[self.yourTableView registerClass:[CustomTableViewCell class] forCellReuseIdentifier:@"CustomCell"];

[self.yourTableView registerNib:[UINib nibWithNibName:@"CustomTableViewCell" bundle:nil] forCellReuseIdentifier:@"CustomCell"];

第 2 步 = 像这样使用 UITableView 的“dequeueReusableCellWithIdentifier:forIndexPath:”方法(为此,您必须注册类或 nib):

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

            cell.imageViewCustom.image = nil; // [UIImage imageNamed:@"default.png"];
            cell.textLabelCustom.text = @"Hello";

            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                // retrive image on global queue
                UIImage * img = [UIImage imageWithData:[NSData dataWithContentsOfURL:     [NSURL URLWithString:kImgLink]]];

                dispatch_async(dispatch_get_main_queue(), ^{

                    CustomTableViewCell * cell = (CustomTableViewCell *)[tableView cellForRowAtIndexPath:indexPath];
                  // assign cell image on main thread
                    cell.imageViewCustom.image = img;
                });
            });

            return cell;
        }

【讨论】:

  • 在最后一个块中调用 cellForRowAtIndexPath 不会导致整个事情第二次被触发吗?
  • @MarkBridges,不。实际上我在这里调用 tableView 的 cellForRowAtIndexPath 方法。不要与 tableView 的同名数据源方法混淆。它需要,它可以被称为 [self tableView: tableView cellForRowAtIndexPath: indexPath];希望这能消除您的困惑。
【解决方案3】:

有多个框架可以解决这个问题。仅举几例:

斯威夫特:

目标-C:

【讨论】:

  • 如果还有其他值得考虑的框架,请添加您的建议。
  • 其实SDWebImage并没有解决这个问题。您可以控制何时下载图像,但SDWebImage 将图像分配给UIImageView 而无需询问您是否允许这样做。基本上,这个库仍然没有解决问题中的问题。
  • 问题的问题是作者没有检查单元格是否被重复使用。这是一个非常的基本问题,这些框架(包括 SDWebImage)都解决了这个问题。
  • SDWebImage 自 iOS 8 以来非常滞后,它是我最喜欢的框架之一,但现在我开始使用 PinRemoteImage,它的效果非常好。
  • @BartłomiejSemańczyk 你说得对,SDWebimage 没有解决这个问题
【解决方案4】:

斯威夫特 3

我使用 NSCache 为图像加载器编写了自己的轻量级实现。 没有单元格图像闪烁!

ImageCacheLoader.swift

typealias ImageCacheLoaderCompletionHandler = ((UIImage) -> ())

class ImageCacheLoader {
    
    var task: URLSessionDownloadTask!
    var session: URLSession!
    var cache: NSCache<NSString, UIImage>!
    
    init() {
        session = URLSession.shared
        task = URLSessionDownloadTask()
        self.cache = NSCache()
    }
    
    func obtainImageWithPath(imagePath: String, completionHandler: @escaping ImageCacheLoaderCompletionHandler) {
        if let image = self.cache.object(forKey: imagePath as NSString) {
            DispatchQueue.main.async {
                completionHandler(image)
            }
        } else {
            /* You need placeholder image in your assets, 
               if you want to display a placeholder to user */
            let placeholder = #imageLiteral(resourceName: "placeholder")
            DispatchQueue.main.async {
                completionHandler(placeholder)
            }
            let url: URL! = URL(string: imagePath)
            task = session.downloadTask(with: url, completionHandler: { (location, response, error) in
                if let data = try? Data(contentsOf: url) {
                    let img: UIImage! = UIImage(data: data)
                    self.cache.setObject(img, forKey: imagePath as NSString)
                    DispatchQueue.main.async {
                        completionHandler(img)
                    }
                }
            })
            task.resume()
        }
    }
}

使用示例

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    let cell = tableView.dequeueReusableCell(withIdentifier: "Identifier")
    
    cell.title = "Cool title"

    imageLoader.obtainImageWithPath(imagePath: viewModel.image) { (image) in
        // Before assigning the image, check whether the current cell is visible
        if let updateCell = tableView.cellForRow(at: indexPath) {
            updateCell.imageView.image = image
        }
    }    
    return cell
}

【讨论】:

  • 我要对你说声谢谢。但代码有点问题。如果让数据=尝试? Data(contentsOf: url) { // 请将 url 替换为 location。它会帮助很多人。
  • 使用原样的代码,您可以通过网络下载两次文件:一次在 downloadTaks 中,一次使用 Data(cntentsOf:)。您必须使用用户位置代替 url,因为下载任务实际上只是通过网络下载并将数据写入临时文件并将 localUrl (在您的情况下为位置)传递给您。所以Data需要指向本地Url,这样它就只从文件中读取。
  • 在使用示例中,它应该是“ImageCacheLoader.obtainImageWithPath(imagePath: viewModel.image).......”吗?
  • 快速滚动时不起作用,由于单元格重复使用,图像会交换多次。
【解决方案5】:

这是 swift 版本(使用@Nitesh Borad 目标 C 代码):-

   if let img: UIImage = UIImage(data: previewImg[indexPath.row]) {
                cell.cardPreview.image = img
            } else {
                // The image isn't cached, download the img data
                // We should perform this in a background thread
                let imgURL = NSURL(string: "webLink URL")
                let request: NSURLRequest = NSURLRequest(URL: imgURL!)
                let session = NSURLSession.sharedSession()
                let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
                    let error = error
                    let data = data
                    if error == nil {
                        // Convert the downloaded data in to a UIImage object
                        let image = UIImage(data: data!)
                        // Store the image in to our cache
                        self.previewImg[indexPath.row] = data!
                        // Update the cell
                        dispatch_async(dispatch_get_main_queue(), {
                            if let cell: YourTableViewCell = tableView.cellForRowAtIndexPath(indexPath) as? YourTableViewCell {
                                cell.cardPreview.image = image
                            }
                        })
                    } else {
                        cell.cardPreview.image = UIImage(named: "defaultImage")
                    }
                })
                task.resume()
            }

【讨论】:

    【解决方案6】:

    最好的答案不是正确的方法:(。您实际上将 indexPath 与模型绑定,这并不总是好的。想象在加载图像期间添加了一些行。现在屏幕上存在给定 indexPath 的单元格,但图像不再正确!这种情况不太可能而且难以复制,但有可能。

    最好使用 MVVM 方法,在控制器中将单元格与 viewModel 绑定并在 viewModel 中加载图像(使用 switchToLatest 方法分配 ReactiveCocoa 信号),然后订阅此信号并将图像分配给单元格! ;)

    您必须记住不要滥用 MVVM。视图必须非常简单!而 ViewModel 应该是可重用的!这就是为什么在控制器中绑定View(UITableViewCell)和ViewModel非常重要。

    【讨论】:

    • 是的,我的索引路径“战术修复”(我不建议这样做,而只是解决 OP 问题的最适度的编辑)会遇到这个问题(但前提是 tableview 继续添加/删除行)。如果这种现象表现出来,我可能会修复其他方式(而不是使用相同的索引路径查找,只需查询模型以获取适当的行)。但是,与您在此处提出的问题相比,该战术解决方案具有更严重的问题(我在上面描述过)。如果您使用我建议的UIImageView 类别解决方案,那么关于索引路径就不存在这样的问题。
    • 我可能听起来有点迂腐,但从 VIEW 调用任何类型的逻辑都是在滥用这种架构。
    【解决方案7】:

    在我的情况下,这不是由于图像缓存(使用 SDWebImage)。这是因为自定义单元格的标签与 indexPath.row 不匹配。

    在 cellForRowAtIndexPath 上:

    1) 为您的自定义单元格分配一个索引值。例如,

    cell.tag = indexPath.row
    

    2) 在主线程上,在分配图像之前,通过与标签匹配来检查图像是否属于相应的单元格。

    dispatch_async(dispatch_get_main_queue(), ^{
       if(cell.tag == indexPath.row) {
         UIImage *tmpImage = [[UIImage alloc] initWithData:imgData];
         thumbnailImageView.image = tmpImage;
       }});
    });
    

    【讨论】:

      【解决方案8】:

      谢谢“Rob”....我在 UICollectionView 上遇到了同样的问题,您的回答帮助我解决了我的问题。 这是我的代码:

       if ([Dict valueForKey:@"ImageURL"] != [NSNull null])
          {
              cell.coverImageView.image = nil;
              cell.coverImageView.imageURL=nil;
      
              dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      
                  if ([Dict valueForKey:@"ImageURL"] != [NSNull null] )
                  {
                      dispatch_async(dispatch_get_main_queue(), ^{
      
                          myCell *updateCell = (id)[collectionView cellForItemAtIndexPath:indexPath];
      
                          if (updateCell)
                          {
                              cell.coverImageView.image = nil;
                              cell.coverImageView.imageURL=nil;
      
                              cell.coverImageView.imageURL=[NSURL URLWithString:[Dict valueForKey:@"ImageURL"]];
      
                          }
                          else
                          {
                              cell.coverImageView.image = nil;
                              cell.coverImageView.imageURL=nil;
                          }
      
      
                      });
                  }
              });
      
          }
          else
          {
              cell.coverImageView.image=[UIImage imageNamed:@"default_cover.png"];
          }
      

      【讨论】:

      • 对我来说,mycell *updateCell = (id)[collectionView cellForItemAtIndexPath:indexPath]; 永远不会是 nil,所以这没有任何作用。
      • 您可以通过以下方式检查您的单元格是否可见:for(mycell *updateCell in collectionView.visibleCells) { cellVisible=YES; } if (cellVisible) { cell.coverImageView.imageURL=[NSURL URLWithString:[Dict valueForKey:@"ImageURL"]]; } 它也适用于我
      • @sneha 是的,您可以通过像这样遍历 visibleCells 来检查它是否可见,但我怀疑使用 [collectionView cellForItemAtIndexPath:indexPath] 更有效(这就是为什么您首先调用地点)。
      • @sneha 另外,顺便说一下,在上面这个答案的代码示例中,您检查updateCell 是否不是nil,但您随后不使用它。您不仅应该使用它来确定集合视图单元格是否仍然可见,还应该在此块内使用updateCell,而不是cell(可能不再有效)。显然,如果是nil,则无需执行任何操作(因为此单元格不可见)。
      【解决方案9】:
       - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
      {
              MyCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
      
              cell.poster.image = nil; // or cell.poster.image = [UIImage imageNamed:@"placeholder.png"];
      
              NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://myurl.com/%@.jpg", self.myJson[indexPath.row][@"movieId"]]];
      
              NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
                  if (data) {
                      UIImage *image = [UIImage imageWithData:data];
                      if (image) {
                          dispatch_async(dispatch_get_main_queue(), ^{
                              MyCell *updateCell = (id)[tableView cellForRowAtIndexPath:indexPath];
                              if (updateCell)
                                  updateCell.poster.image = image;
                          });
                      }
                  }
              }];
              [task resume];
      
              return cell;
          }
      

      【讨论】:

        【解决方案10】:

        我认为您想在为后台单元格加载图像时加快单元格加载速度。为此,我们做了以下步骤:

        1. 检查文件目录是否存在文件。

        2. 如果不是,则第一次加载图像,并将其保存到 我们的电话文件目录。如果您不想将图片保存在手机中,则可以直接在后台加载单元格图片。

        3. 现在加载过程:

        只需包括:#import "ManabImageOperations.h"

        单元格的代码如下所示:

        NSString *imagestr=[NSString stringWithFormat:@"http://www.yourlink.com/%@",[dictn objectForKey:@"member_image"]];
        
                NSString *docDir=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0];
                NSLog(@"Doc Dir: %@",docDir);
        
                NSString  *pngFilePath = [NSString stringWithFormat:@"%@/%@",docDir,[dictn objectForKey:@"member_image"]];
        
                BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:pngFilePath];
                if (fileExists)
                {
                    [cell1.memberimage setImage:[UIImage imageWithContentsOfFile:pngFilePath] forState:UIControlStateNormal];
                }
                else
                {
                    [ManabImageOperations processImageDataWithURLString:imagestr andBlock:^(NSData *imageData)
                     {
                         [cell1.memberimage setImage:[[UIImage alloc]initWithData: imageData] forState:UIControlStateNormal];
                        [imageData writeToFile:pngFilePath atomically:YES];
                     }];
        }
        

        ManabImageOperations.h:

        #import <Foundation/Foundation.h>
        
            @interface ManabImageOperations : NSObject
            {
            }
            + (void)processImageDataWithURLString:(NSString *)urlString andBlock:(void (^)(NSData *imageData))processImage;
            @end
        

        ManabImageOperations.m:

        #import "ManabImageOperations.h"
        #import <QuartzCore/QuartzCore.h>
        @implementation ManabImageOperations
        
        + (void)processImageDataWithURLString:(NSString *)urlString andBlock:(void (^)(NSData *imageData))processImage
        {
            NSURL *url = [NSURL URLWithString:urlString];
        
            dispatch_queue_t callerQueue = dispatch_get_main_queue();
            dispatch_queue_t downloadQueue = dispatch_queue_create("com.myapp.processsmagequeue", NULL);
            dispatch_async(downloadQueue, ^{
                NSData * imageData = [NSData dataWithContentsOfURL:url];
        
                dispatch_async(callerQueue, ^{
                    processImage(imageData);
                });
            });
          //  downloadQueue=nil;
            dispatch_release(downloadQueue);
        
        }
        @end
        

        如果有任何问题请查看答案并评论......

        【讨论】:

          【解决方案11】:

          只需改变,

          dispatch_async(kBgQueue, ^{
               NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:   [NSString stringWithFormat:@"http://myurl.com/%@.jpg",[[myJson objectAtIndex:indexPath.row] objectForKey:@"movieId"]]]];
               dispatch_async(dispatch_get_main_queue(), ^{
                  cell.poster.image = [UIImage imageWithData:imgData];
               });
           });
          

          进入

              dispatch_async(kBgQueue, ^{
                   NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:   [NSString stringWithFormat:@"http://myurl.com/%@.jpg",[[myJson objectAtIndex:indexPath.row] objectForKey:@"movieId"]]]];
                   cell.poster.image = [UIImage imageWithData:imgData];
                   dispatch_async(dispatch_get_main_queue(), ^{
                      [self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
                   });
               });
          

          【讨论】:

            【解决方案12】:

            您可以只传递您的网址,

            NSURL *url = [NSURL URLWithString:@"http://www.myurl.com/1.png"];
            NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data,    NSURLResponse * _Nullable response, NSError * _Nullable error) {
                if (data) {
                    UIImage *image = [UIImage imageWithData:data];
                    if (image) {
                        dispatch_async(dispatch_get_main_queue(), ^{
                                yourimageview.image = image;
                        });
                    }
                }
            }];
            [task resume];
            

            【讨论】:

            • 我可以知道原因吗?
            【解决方案13】:
            -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
                Static NSString *CellIdentifier = @"Cell";
                QTStaffViewCell *cell = (QTStaffViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
            
                If (cell == nil)
                {
            
                    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"QTStaffViewCell" owner:self options:nil];
                    cell = [nib objectAtIndex: 0];
            
                }
            
                StaffData = [self.staffArray objectAtIndex:indexPath.row];
                NSString *title = StaffData.title;
                NSString *fName = StaffData.firstname;
                NSString *lName = StaffData.lastname;
            
                UIFont *FedSanDemi = [UIFont fontWithName:@"Aller" size:18];
                cell.drName.text = [NSString stringWithFormat:@"%@ %@ %@", title,fName,lName];
                [cell.drName setFont:FedSanDemi];
            
                UIFont *aller = [UIFont fontWithName:@"Aller" size:14];
                cell.drJob.text = StaffData.job;
                [cell.drJob setFont:aller];
            
                if ([StaffData.title isEqualToString:@"Dr"])
                {
                    cell.drJob.frame = CGRectMake(83, 26, 227, 40);
                }
                else
                {
                    cell.drJob.frame = CGRectMake(90, 26, 227, 40);
            
                }
            
                if ([StaffData.staffPhoto isKindOfClass:[NSString class]])
                {
                    NSURL *url = [NSURL URLWithString:StaffData.staffPhoto];
                    NSURLSession *session = [NSURLSession sharedSession];
                    NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url
                            completionHandler:^(NSURL *location,NSURLResponse *response, NSError *error) {
            
                  NSData *imageData = [NSData dataWithContentsOfURL:location];
                  UIImage *image = [UIImage imageWithData:imageData];
            
                  dispatch_sync(dispatch_get_main_queue(),
                         ^{
                                cell.imageView.image = image;
                          });
                }];
                    [task resume];
                }
                   return cell;}
            

            【讨论】:

            • 没有任何解释的代码转储很少有帮助。请考虑编辑此答案以提供一些上下文。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2016-06-27
            • 1970-01-01
            • 2023-04-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多