从UITableView单元内的URL加载异步图像-滚动时图像变为错误图像


158

我编写了两种方法来异步加载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;
}

5
您正在尝试将信息存储在实际单元格中。这很糟糕,非常糟糕。您应该将信息存储在n个数组(或类似的东西)中,然后将其显示在单元格中。这种情况下的信息是实际的UIImage。是的,异步加载它,但是将其加载到数组中。
2013年

1
@Fogmeister您是指poster什么?大概是他自定义单元中的imageview,所以EXEC_BAD_ACCESS所做的事情是完全正确的。您是正确的,不应将单元格用作模型数据的存储库,但是我不认为这就是他的工作。他只是给自定义单元提供自己需要的内容。此外,这是一个更微妙的问题,对于将图像本身存储在支持表视图的模型数组中,我会有所警惕。最好使用图像缓存机制,并且模型对象应该从该缓存中检索。
罗布

1
是的,正是我的意思。查看请求(完整显示),他正在异步下载图像并将其直接放入单元格的imageView中。(因此使用单元格来存储数据,即图像)。他应该做的是引用一个对象并向该对象请求图像(包含在数组中或某处)。如果对象还没有图像,则应返回一个占位符并下载图像。然后,当下载图像并准备显示图像时,请告知表格,以便它可以更新单元格(如果可见)。
Fogmeister

1
每当滚动到表中的该单元格时,他所做的都会强制下载。图像是否持久存储取决于他,但是至少要在表视图的生命周期内存储它们。
Fogmeister

1
正是:D这样,您只需从URL提取图像一次。您将在Facebook Friend Picker之类的东西上看到此信息。启动时,所有的头像都是灰色的占位符。然后,当您滚动时,它们全部随其移动而填充。但是,当您滚动回到先前显示的单元格时,它将立即显示已经下载的图像。
Fogmeister

Answers:


230

假设您正在寻找一种快速的战术解决方案,那么您需要做的是确保单元格图像已初始化,并且该单元格的行仍然可见,例如:

- (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. 您不会在启动后台请求之前初始化单元格图像(这意味着在下载新图像时,出队单元格的最后一张图像仍然可见)。确保任何图像视图nilimage属性,否则您将看到图像闪烁。

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

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

  3. 与当前的问题无关,我仍然感到不得不将其更新为利用现代约定和API,特别是:

    • 使用NSURLSession而不是调度-[NSData contentsOfURL:]到后台队列;

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

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

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

  1. 上面的代码未缓存下载的图像。这意味着,如果您在屏幕外滚动图像然后又回到屏幕上,则该应用程序可能会尝试再次检索图像。也许您很幸运,您的服务器响应标头将允许NSURLSessionand 提供的相当透明的缓存NSURLCache,但是如果不这样做,您将发出不必要的服务器请求并提供慢得多的UX。

  2. 我们不会取消对滚动到屏幕之外的单元格的请求。因此,如果您快速滚动到第100行,则该行的图像可能会积压在对甚至不再可见的前99行的请求之后。您始终要确保为最佳UX优先考虑对可见单元的请求。

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


1
谢谢。我相信您需要编辑答案。updateCell.poster.image = nilto cell.poster.image = nil;updateCell在声明之前被调用。
塞格夫

1
我的应用程序使用了很多json,因此AFNetworking绝对是可行的方法。我知道它,但懒得使用它。我只是欣赏缓存如何使用其简单的代码行工作。[imageView setImageWithURL:<#(NSURL *)#> placeholderImage:<#(UIImage *)#>];
塞格夫

2
对以上所有内容和SDWebImage进行了尝试(实际上已在此处停止,甚至不需要尝试AFNetworking),这是迄今为止的最佳选择。谢谢@Rob。
mondousage

1
完成加载图像并更新图像视图后,请卸下活动指示器。唯一的窍门是,您必须预料如果在检索图像时单元格滚动出视线并且单元格再次用于另一行时会发生什么情况,那么您将必须检测到任何现有活动指示器的存在并进行删除/更新不仅是假设单元格没有现有指标。
罗布(Rob)2013年

1
最初的问题是“为什么cellForRowAtIndexPath当我快速滚动时会导致图像闪烁”,我解释了为什么会发生这种现象以及如何解决它。但是我继续解释了为什么这还不够,描述了一些更深层次的问题,并争辩说为什么最好使用这些库之一更好地处理此问题(优先处理对可见单元的请求,缓存以避免冗余网络)请求等)。我不清楚您对“如何停止表格视图中闪烁的图像”这一问题还有什么期望。
罗布

15

/ *我已经这样做了,并且也对其进行了测试* /

步骤1 =在viewDidLoad方法中为表注册自定义单元格类(如果是表中的原型单元格)或笔尖(如果是自定义单元格,则是自定义单元格):

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

要么

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

步骤2 =像这样使用UITableView的“ dequeueReusableCellWithIdentifier:forIndexPath:”方法(为此,您必须注册class或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;
        }

1
不会在最后一个块中调用cellForRowAtIndexPath导致整个事件再次被触发吗?
Mark Bridges

@MarkBridges,否。实际上,我在这里调用tableView的cellForRowAtIndexPath方法。不要与同名的tableView的datasource方法混淆。它需要,可以像[self tableView:tableView cellForRowAtIndexPath:indexPath]一样调用;希望这能消除您的困惑。
Nitesh Borad

14

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

迅速:

目标C:


如果还有其他值得考虑的框架,请添加您的建议。
kean 2015年

3
其实并SDWebImage不能解决这个问题。您可以控制何时下载图像,但是无需向您询问是否允许这样做即可SDWebImage将图像分配给该图像UIImageView。基本上,问题仍然无法使用该库解决。
巴特洛梅耶Semańczyk

这个问题的问题在于作者没有检查单元是否被重用。这是一个非常基本的问题,这些问题包括SDWebImage都可以解决。
kean 2015年

自iOS 8以来,SDWebImage一直很落后,它是我最喜欢的框架之一,但现在我开始使用PinRemoteImage,它的确非常好。
琼·卡多纳

@BartłomiejSemańczyk是的,SDWebimage无法解决此问题
1

9

迅捷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
}

3
我会说谢谢你。但是代码有点问题。如果让数据=尝试?Data(contentsOf:url){//请替换url到位置。这会帮助很多人。

2
使用原样的代码,您可以通过网络下载两次文件:一次是在downloadTaks中,一次是与Data(cntentsOf :)。您必须使用用户位置代替url,因为下载任务实际上只是通过网络下载并将数据写入临时文件,然后将localUrl传递给您(在您的情况下为位置)。因此,数据需要指向本地Url,以便仅从文件读取。
斯特凡·卢卡

在使用示例中,是否应该为“ ImageCacheLoader.obtainImageWithPath(imagePath:viewModel.image).......”?
蒂姆·克鲁格

快速滚动时将无法使用,由于单元重用,图像将交换很多次。
Juan Boero

5

这是快速版本(通过使用@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()
            }

3

最好的答案不是执行此操作的正确方法:(。您实际上将indexPath与模型绑定在一起,这并不总是那么好。想象一下,在加载图像时添加了一些行。现在,给定indexPath的单元格在屏幕上,但是图像不再正确!这种情况不太可能,很难复制,但有可能。

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

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


1
是的,我的索引路径“战术修复”(我不建议这样做,而只是解决OP问题的最适度的编辑)受此问题困扰(但前提是当tableview继续添加/删除行时)。如果这种现象表现出来,我可以用其他方法解决(而不是使用相同的索引路径来查找,而只是查询模型以查找适当的行)。但是,这种战术上的解决方法比您在此处提出的解决方案还要严重得多(我在上面描述了)。如果您使用UIImageView我建议的类别解决方案,那么关于索引路径就不会存在此类问题。
罗布2014年

2
我可能听起来有些书呆子,但是从VIEW调用任何一种逻辑都在滥用这种体系结构。
badeleux 2014年

3

就我而言,这不是由于图像缓存(使用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;
   }});
});

2

谢谢“ 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];永远不会为零,所以这没有任何作用。
carbocation 2014年

1
您可以通过以下方法检查单元格是否可见:for(mycell * collectionView.visibleCells中的updateCell){cellVisible = YES; } if(cellVisible){cell.coverImageView.imageURL = [NSURL URLWithString:[Dict valueForKey:@“ ImageURL”]] ;; }它也对我
有用

@sneha是的,您可以通过迭代遍历来检查它是否可见visibleCells,但是我怀疑使用[collectionView cellForItemAtIndexPath:indexPath]它的效率更高(这就是为什么您首先进行该调用的原因)。
罗布

@sneha另外,顺便说一句,在上面这个答案的代码示例中,您检查是否updateCell不是nil,但是然后不要使用它。您不仅应该使用它来确定集合视图单元格是否仍然可见,而且还应该updateCell在此块内部使用它,否则cell(可能不再有效)。显然,如果是,则nil无需执行任何操作(因为此单元格不可见)。
罗布

2
 - (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;
    }

0

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

  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

如果有任何问题,请检查答案并发表评论。


0

只需更改,

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];
         });
     });

0

您只需传递您的网址,

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];

我可以知道原因吗?
User558

-1
-(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;}

2
没有任何解释的代码转储很少有帮助。请考虑编辑此答案以提供一些上下文。
克里斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.