将包含url的数组中的图片添加到单元格的imageView - iOS Xcode Obj C

Posted

技术标签:

【中文标题】将包含url的数组中的图片添加到单元格的imageView - iOS Xcode Obj C【英文标题】:Adding pictures from array containing url's to imageView of cell - iOS Xcode Obj C 【发布时间】:2015-05-03 13:36:38 【问题描述】:

为了更好地了解这篇文章的内容,它最终以这个问题结束:

如何从任意数量的 url 异步下载未预定义数量的图像,将它们添加到字典(或数组,如果这样更容易的话),以便按照下载开始的顺序添加它们,而不是添加它们按完成的顺序?

这是这篇文章的核心问题,但是为了采取良好的措施并让人们真正理解我的意思,我在下面的文章中添加了涉及这个问题的具体案例。我知道这很长,但解释我的情况非常复杂。

所以,这里什么都没有:

我有一个 tableView,它从不同的数组中加载了一些东西。每次启动应用程序时,所有数组都是从 JSON 提取创建的,其想法是我可以通过简单地更新 JSON 文本文件来更新应用程序中的信息。 JSON 文本文件中的条目之一包含图像的 url,我想将其添加到 tableView 单元格的 contentview 中。我得到了 JSON 提取工作,并创建了一个包含提取信息的字典“dataDictionary”。

然后,像这样创建数组:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection 

   // Array is created from the dictionary created from the JSON fetch

    _dataArray = _dataDictionary[@"games"];

   // Arrays that will eventually hold info from JSON file is created

    _Titles = [[NSMutableArray alloc] init];
    _Descriptions = [[NSMutableArray alloc] init];
    _Date = [[NSMutableArray alloc] init];

    //images will be added to dictionary instead
    _imageDict = [[NSMutableDictionary alloc]init];


   // Info parsed from the JSON fetch, is now added to arrays

for (NSDictionary *eachData in _dataArray)
    
        _Title = eachData[@"Title"];
        _Description = eachData[@"Description"];
        _Date = eachData[@"Release"];

   // Key for image url is created
        _image = eachData[@"ImageLink"];


        [_Titles addObject:_Title];
        [_Descriptions addObject:_Description];
        [_Dates addObject:_Date];

现在下面有更多代码,这是我处理图像的地方(将在这个简短的解释和下面的代码示例之后出现),正如你所见,我已经为 ImageLink 中的条目指定了一个名为“image”的键JSON。现在我调用这个方法,开始异步下载图片:

- (void)downloadImageWithURL:(NSURL *)url completionBlock:(void (^)  (BOOL succeeded, UIImage *image))completionBlock

    NSMutableURLRequest *request = [NSMutableURLRequest    requestWithURL:url];
    [NSURLConnection sendAsynchronousRequest:request
                                   queue:[NSOperationQueue mainQueue]
                       completionHandler:^(NSURLResponse *response,  NSData *data, NSError *error) 
                               if ( !error )
                               
                                   UIImage *image = [[UIImage alloc]  initWithData:data];
                                   completionBlock(YES,image);
                                else
                                   completionBlock(NO,nil);
                               
                           ];



这个方法在下面的代码中被调用,所以继续之前的代码:

// Just to be clear, we are now back in the "for (NSDictionary *eachData in _dataArray)" statement from the first code sample


//calling the above method here

[self downloadImageWithURL:[NSURL URLWithString:_image] completionBlock:^(BOOL succeeded, UIImage *image) 
            if (succeeded) 

//All this code runs when an image is successfully downloaded
NSLog(@"image download succesfull");

                UIImage *downloadedImage = [[UIImage alloc] init];

                // Doing this so I can resize the downloaded image to a proper size
                downloadedImage = image;

                //long boring code to resize image here


                UIImage *resizedImage = [[UIImage alloc] init];


                // resizedImage is as the name implies, the resized image that now has a proper size for the cell's content view



                 _number++; //Int that start out as -1, this increments each time an image is downloaded to allow the images to be added to the dictionary with the keys 0, 1, 2...

                // Wrapping the int in a NSNumber
                NSNumber *number = [NSNumber numberWithInt:_number];

                // Turning the NSnumber into a string
                NSString *key = [number stringValue];


                // Images finally being added to the dictionary, but, in the wrong "order" - or rather, with the wrong keys / numbers
                [_imageDict setObject:resizedImage forKey:key];




                //Update tableView when images are added to dictionary
                if (_imageDict.count == _gameTitles.count) 
                [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];
                


            
        ];







      //Update tableView when data is added to array, this is to allow info to be shown even before the images are done downloading
        if (Titles.count == _dataArray.count) 


            // Update tableView with loaded content
            [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];

            
         // "for" statement ends here

总结最重要的部分:

在 tableView 中填充单元格时,我使用 indexPath.row 从每个数组中获取正确的信息。 图像下载,我将它们添加到带有键 0、1、2 的字典中。但是,当完成时,因为下载是异步的,所以下载不会按照初始化的顺序完成,而是较小的图像得到 (毫不奇怪)首先下载并添加到图像字典中。这意味着图像的键不符合我所有其他数组中的顺序,因此使用 indexPath.row 在第一个单元格中设置图像只是设置首先下载的图像,而不是首先开始的图像下载.基本上,即使先下载图像 5,也应该添加键“4”,而不是“0”。

我还应该提到,由于 JSON 提取,我不知道会预先下载多少张图片,因为这可能会根据 JSON 的不同而改变。这抵消了 *** 上的很多答案(以及其他地方)。

所以所有这些文本和代码以及诸如此类的东西都会导致开始的问题,我如何从任意数量的 url 异步下载未预定义数量的图像,将它们添加到字典(或数组,如果这更容易的话),以便它们按开始下载的顺序添加,而不是按完成顺序添加?

非常感谢您花时间阅读本文。

【问题讨论】:

Apple 提供了异步图像下载的示例代码,并按顺序下载以初始化您可以递归方法,因此开始第一个图像下载,下载完成后开始第二个方法的下载。链接:developer.apple.com/library/ios/samplecode/LazyTableImages/… 我真的很喜欢这种方法,但是我现在对这种方法已经摆弄太多了,所以我真的希望它现在可以工作.. pandaren codemaster 建议的 SDWebImage Library 似乎很多更容易实现,特别是考虑到我已经编写的代码,但我将来可能会使用这种方法,这样我就可以更容易地实现它,而不必重新考虑我的代码;)但无论如何感谢!我不知道 Apple 制作了这样的示例代码。 @Chikara 您可以接受我的回答,以供将来参考,如果它是您的解决方案。 @pandarencodemaster 该死的,直到现在我还以为是Malay Soni 写了那条评论!我接受了你的回答,因为我非常接近按照你的方法/答案让它按我的意愿工作 【参考方案1】:

所以所有这些文本和代码以及诸如此类的东西都会导致开始的问题,我如何从任意数量的 url 异步下载未预定义数量的图像,将它们添加到字典(或数组,如果这更容易的话),以便它们按开始下载的顺序添加,而不是按完成顺序添加?

您可以使用SDWebImage Library 下载图片,它从给定的 URL 异步下载您的图片,它还缓存图片并为您管理所有内容,它非常受欢迎。您所要做的就是添加以下代码到你的-cellForRowAtIndexPath 委托方法:

[cell.imageView sd_setImageWithURL:[NSURL URLWithString:imageURLThatYouGetFromJSONResponse]
                  placeholderImage:[UIImage imageNamed:@"placeholder.png"]];

另外我建议你不要使用数组或字典。你可以将所有信息存储在一个对象中。在许多情况下它是简单和最佳实践的,被称为面向对象的方法。你创建一个 NSObject 子类并在那里创建属性“title”,“description”,“name”,“imageURL”。您可以关注this OOP Tutorial以获得更好的理解。

你不关心图片的数量,因为你在-numberOfRowsInSection中定义了行数,-cellForRowAtIndexPath被称为你在-numberOfRowsInSection中写的次数。你可以添加:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section  
    return [arrayOfYourObjects count];

编辑:如何在使用 SDWebImage 时操作图像?

回答:你可以使用 SDWebImageManager,它也为你管理缓存

SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadImageWithURL:imageURL
                      options:0
                     progress:^(NSInteger receivedSize, NSInteger expectedSize) 
                         // progression tracking code
                     
                     completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) 
                         if (image) 
                             // Code to resize
                             //After resizing
                             cell.imageView.image = resizedImage;
                         
                     ];

【讨论】:

好的,我得到了它的工作,但是我也希望它像以前一样调整图像的大小,我如何能够在将图像显示在表格中之前对其进行操作?另外,我怎样才能自动更新表格,这样您就不必滚动单元格然后进入视图以将图像加载到单元格中? 顺便说一句,感谢您对 NSObject 子类的提醒,我从来没有真正理解过 NSObject 是什么,现在我知道了 ;) 另外,我怎样才能自动更新表格,这样您就不必滚动单元格然后进入视图以将图像加载到单元格中?对不起,但我不明白这一点?我编辑了答案以显示在何处以及如何调整图像大小。 @Chikara 谢谢!但是,占位符图像呢?现在我正在创建一个 UIImage 并将 cell.imageView.image 设置为该图像,直到图像加载完毕,您建议在使用 SDWebImageManager 时如何处理? 详细说明关于使用图像更新表格的部分 - 我的意思是图像不会自行加载到单元格中,您必须将单元格滚动到视图中,然后从查看,然后再次进入。这是因为当单元格出现在屏幕上时,开始下载图像的代码,但是没有运行任何代码来使用图像更新 tableview,因此单元格必须退出并重新进入视图,以便它们使用适当的图片进行更新.

以上是关于将包含url的数组中的图片添加到单元格的imageView - iOS Xcode Obj C的主要内容,如果未能解决你的问题,请参考以下文章

如何将 UIImages 数组添加到集合视图

将数组中的图像嵌入到集合视图的单元格中

如何从动态创建的表格视图单元格的输入字段中将数据检索到数组

将字符串添加到LOOP中同一单元格的下一行。

设置添加到表格视图中单元格的图像的大小

如何将自定义样式添加到 Angular Material Table 单元格的一小部分?