【问题标题】:Store images from web for UITableView为 UITableView 存储来自网络的图像
【发布时间】:2012-07-18 05:33:48
【问题描述】:

我有一个带有自定义单元格的 UITableView,其中显示了文本和图像。(右侧的图像)。 起初,一切正常,但后来我注意到滚动时,整个 tableview 都滞后了。这是因为当重新使用它们时,该应用程序可能会在单元格进入屏幕时下载单元格图像。我添加了一个 NSCache 来存储下载的图像,并告诉 cellForRowAtIndexPath 加载 imageName(如果存在),否则,再次从 Internet 下载。但是,缓存是短期存储,如果我用主页按钮退出我的应用程序,然后重新进入,那么只剩下一些图像,并且必须重新下载图像。

我正在尝试找出比使用缓存更长期存储图像的最佳方法。我已经阅读了一些关于 NSDirectory 和存储在应用程序库中的信息,但还没有弄清楚..

我相信,最合乎逻辑的解决方案是这样做:

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

     /*stuff*/

    NSString *imageName = [dictionary objectForKey:@"Image"];
    UIImage *image = [--Cache-directory-- objectForKey:imageName]; //Try to get it from cache

    if(image) //If image was received from cache:
    {
        cell.imageView.Image = image;
    }
    else //If not in cache:
    {
        image = [--local-directory-- objectForKey:imageName]; //Check some local directory

        if(image) //If image received from directory:
        {
            cell.imageView.Image = image;
            // + Also save it to the cache?
        }
        else //If image was in neither cache or local directory, get it from the website with given URL
        {
            image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:imageURL]];
            //Then save to cache and to file?
        }
    }


}

图像很少更改或切换出来,但我愿意事先在应用程序中实现它们,这样我每次添加图像时都必须发布更新。

这对我来说是合乎逻辑的。我在正确的轨道上吗?以及如何“调用”本地目录?就像,如果我将图像添加到 NSDirectory 对象或其他东西,这不是每次都会重置吗?如何访问本地文件夹?

【问题讨论】:

    标签: iphone ios image uitableview web


    【解决方案1】:

    您从错误的方向解决问题...
    问题是 tableView 滞后。
    原因是您在主线程中下载图像。
    即使您从光盘中读取图像,您仍然无法获得最佳滚动性能。

    所以不能阻塞你的主线程。为此,您可以异步下载图片,或使用 GCD 在另一个线程中下载。

    像这样:

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
    
         /*stuff*/  
    
         // this will block your main thread, bad idea..
         //image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:imageURL]];
    
         // instead call it in a separate thread  
         dispatch_queue_t imgDownloaderQueue = dispatch_queue_create("imageDownloader", NULL);
             dispatch_async(imgDownloaderQueue, ^{
             // download the image in separate thread
             UIImage *image = [[[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:imageURL]] autorelease];
             dispatch_queue_t main_queue = dispatch_get_main_queue();
             dispatch_sync(main_queue, ^{                        
                // this is called in the main thread after the download has finished, here u update the cell's imageView with the new image
                UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
                cell.imageView.image = image;
            });
        });
        dispatch_release(imgDownloaderQueue);
    }
    

    【讨论】:

      【解决方案2】:

      没有必要保存所有这些图像。

      如果您从 url 获取图片,则只需使用 AsyncImageLoader

      使用该链接获取 ASyncImageView 的 h 和 m 文件并将它们保存在您的项目中。

      在您执行此操作的地方导入 h 文件

      #import 'AsyncImageView.h'
      

      然后使用下面的代码

      [[AsyncImageLoader sharedLoader] cancelLoadingURL:image.imageURL];
      
      image.imageURL=[NSURL URLWithString:@"imageURL"];
      

      【讨论】:

      • 好的,我试试这个,但这不会导致每次启动时tableview都是空的,并最终填满它吗?图片将很少更新,除了第一次启动应用程序时,我不想看到没有图片的单元格..
      • 或者至少,每次你退出这个过程时,它似乎......这实际上工作得很好!
      • 有没有办法让图像在准备好后立即加载,而不是在单元格重新进入屏幕之前不加载?或者我可以在 viewDidLoad 中设置前 8 行的图像吗?
      • 你可以在viewDidLoad中设置前8行的图片。 cancelLoadingUrl 所做的只是确保它在添加之前不存在。
      【解决方案3】:

      我最近在 UITableView 上工作,我遇到了类似的问题。我使用的一个解决方法是因为图像很小(我假设你的也是)并且具有唯一的名称,我将它们存储在应用程序的本地文档文件夹中,并使用核心数据来保存对图像位置的引用。我从那个位置加载图像。如果图像改变,我改变那些图像。不过,这可能不是最优雅的方法。只是一个建议。

      至于访问本地app目录,我用这个

      // INSTANCE METHOD - get the path and file name for the saved plist
      - (NSString *)getFileLocation:(NSString *)location {
          // Get all available file system paths for the user
          NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
      
          // Get the "Documents" directory path - it's the one and only on iPhone OS
          NSString *documentsPath = [paths objectAtIndex:0];
      
          // Specify the file name, appending it to documentsPath above
          NSString *savedFileName = [documentsPath stringByAppendingPathComponent:location];
          NSLog(@"File Location %@", location);
          // We're done
          return savedFileName;
      }
      

      【讨论】:

      • 刚刚记住:当你做一个 web 服务拉取时,你不是以某种格式(如 JSON)在本地存储所有数据吗?如果是这样,图像不是以 JSON 值中的二进制数据形式返回吗?如果是这样,那么我不明白为什么每次回收单元时都应该重新加载图像。如果您不将数据与您的网络服务一起返回,也许您可​​以尝试一下,看看您是否获得了更好的响应时间。
      • 我使用 JSON 是的,但只是为了获取图像的名称(以及其他数据(标题,描述)),例如“1.png,2.png”等。然后我得到图像with UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[@"www.example.com/%@", imageName]];好吧,不完全是这样,但也有一些字符串解析的东西......但我只是得到了名字。我应该将整个图像保存在数据库中吗?最多可能是 50 张 500*150 像素的图片。
      【解决方案4】:

      似乎最好的解决方案可能是使用 Core Data。但与@Anachid 建议的不同,您可以像以前一样从互联网加载它们并将它们放入核心数据目录。我建议您上网而不是将它们放入您的解决方案中,因为您必须更新才能获取新图片。然后,您可以根据需要使用它们。

      【讨论】:

      • 好吧.. 是的,我同意。我试图阅读一些核心数据,但我发现很难快速学习。你知道任何教程,或者关于它的简短但透彻的文章吗?我不知道从哪里开始,我想我误解了 Core Data 的使用,以及它实际上是什么..
      • 基本上,您设置数据模型,然后通过您的应用程序用您的数据填充它。这意味着您的应用程序需要在每次新启动时(从网络)获取图片,并将它们加载到核心数据中。从那时起,您就可以从中获取数据。那里有很多关于它的材料,这取决于你想要什么,我在 youtube 上看到了一些很好的教程。
      猜你喜欢
      • 2022-01-22
      • 2010-11-17
      • 2014-03-19
      • 1970-01-01
      • 2013-05-02
      • 1970-01-01
      • 2011-04-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多