在iOS应用程序上缓存图像的最佳方法?

2020/12/07 01:22 · ios ·  · 0评论

不久,我有一个NSDictionary带URL的图片,需要显示在中UITableView每个单元格都有标题和图像。我成功地做到了这一点,尽管滚动是滞后的,因为似乎细胞每次进入屏幕时都会下载图像。我搜索了一下,发现SDWebImage在github上 这使滚动滞后消失了。我不完全确定它做了什么,但是我相信它做了一些缓存。但!每次我第一次打开该应用程序时,都看不到任何图像,因此必须向下滚动并备份以使其到达。而且,如果我使用主页按钮退出应用程序并再次打开,则似乎缓存正在运行,因为屏幕上的图像可见,但是,如果我向下滚动一个单元格,则下一个单元格将没有图像。直到我滚动过去并备份,或者单击它。这是应该如何工作的缓存吗?或缓存从网络下载的图像的最佳方法是什么?图像正在大量更新,因此我几乎只是将其导入到项目中,但是我希望可以在不上传更新的情况下更新图像。

启动时是否不可能从缓存中加载整个表视图的所有图像(假设缓存中有东西)?这就是为什么我有时会看到没有图像的单元格吗?

是的,我很难理解什么是缓存。

- 编辑 -

我仅使用相同大小(500x150)的图像尝试了此操作,并且宽高比错误消失了,但是当我向上或向下滚动时,所有单元格上都有图像,但是起初它们是错误的。单元格进入视图几毫秒后,将显示正确的图像。这真是令人讨厌,但也许是必须的吗?..似乎它首先是从缓存中选择了错误的索引。如果我缓慢滚动,则可以看到图像从错误的图像闪烁到正确的图像。如果我快速滚动,那么我相信在任何时候都可以看到错误的图像,但是由于快速滚动,我无法分辨。当快速滚动变慢并最终停止时,错误的图像仍会出现,但是在停止滚动后立即更新为正确的图像。我也有习惯UITableViewCell类,但是我还没有做任何大的改变。.我还没有遍历我的代码,但是我想不出什么错。.也许我的顺序错了..我在Java,C#,PHP等中进行了很多编程,但是我很难理解Objective-c,.h以及.m...。

@interface FirstViewController : UITableViewController{

/**/
NSCache *_imageCache;
}

(以及其他变量)在中FirstViewController.h这不正确吗?

这是我的cellForRowAtIndexPath

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"hallo";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

if (cell == nil)
{
    cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}

NSMutableArray *marr = [hallo objectAtIndex:indexPath.section];
NSDictionary *dict = [marr objectAtIndex:indexPath.row];

NSString* imageName = [dict objectForKey:@"Image"];
//NSLog(@"url: %@", imageURL);

UIImage *image = [_imageCache objectForKey:imageName];

if(image)
{
    cell.imageView.image = image;
}
else
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        NSString* imageURLString = [NSString stringWithFormat:@"example.com/%@", imageName];
        NSURL *imageURL = [NSURL URLWithString:imageURLString];
        UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:imageURL]];

        if(image)
        {
            dispatch_async(dispatch_get_main_queue(), ^{
                CustomCell *cell =(CustomCell*)[self.tableView cellForRowAtIndexPath:indexPath];
                if(cell)
                {
                    cell.imageView.image = image;
                }
            });
            [_imageCache setObject:image forKey:imageName];
        }
    });
}

cell.textLabel.text = [dict objectForKey:@"Name"];

return cell;
}

缓存只是意味着保留所需数据的副本,这样就不必从速度较慢的源中加载数据。例如,微处理器通常具有高速缓存,在高速缓存中保存数据的副本,因此不必访问RAM,这会慢很多。硬盘通常具有内存缓存,从中文件系统可以更快地访问最近访问的数据块。

同样,如果您的应用程序从网络上加载了很多图像,则可能需要将它们缓存在设备上,而不是每次需要时都下载它们。有很多方法可以做到这一点-听起来您已经找到了一种。您可能希望将下载的图像存储在应用程序的/ Library / Caches目录中,尤其是在您不希望它们更改的情况下。从二级存储加载图像比通过网络加载图像要快得多。

您可能还对鲜为人知的NSCache感兴趣,可以将所需的图像保存在内存中。NSCache就像字典一样工作,但是当内存紧张时,它将开始释放其某些内容。您可以先检查给定图像的缓存,如果找不到该图像,则可以在缓存目录中查找,如果找不到该图像,则可以下载。第一次运行时,这一切都不会加快图像加载速度,但是一旦您的应用下载了大部分所需内容,它的响应速度就会大大提高。

我认为Caleb很好地回答了缓存问题。在检索图像时,我只是要介绍更新UI的过程,例如,假设您有一个NSCache名为的图像_imageCache

首先,为表视图定义一个操作队列属性:

@property (nonatomic, strong) NSOperationQueue *queue;

然后在中viewDidLoad,初始化它:

self.queue = [[NSOperationQueue alloc] init];
self.queue.maxConcurrentOperationCount = 4;

然后在中cellForRowAtIndexPath,您可以:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"ilvcCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    // set the various cell properties

    // now update the cell image

    NSString *imagename = [self imageFilename:indexPath]; // the name of the image being retrieved

    UIImage *image = [_imageCache objectForKey:imagename];

    if (image)
    {
        // if we have an cachedImage sitting in memory already, then use it

        cell.imageView.image = image;
    }
    else
    {
        cell.imageView.image = [UIImage imageNamed:@"blank_cell_image.png"];

        // the get the image in the background

        [self.queue addOperationWithBlock:^{

            // get the UIImage

            UIImage *image = [self getImage:imagename];

            // if we found it, then update UI

            if (image)
            {
                [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                    // if the cell is visible, then set the image

                    UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
                    if (cell)
                        cell.imageView.image = image;
                }];

                [_imageCache setObject:image forKey:imagename];
            }
        }];
    }

    return cell;
}

我之所以仅提及这一点,是因为最近我看到一些代码示例在SO上浮动,这些示例使用GCD更新适当的UIImageView image属性,但是在将UI更新分派回主队列的过程中,他们采用了奇怪的技术(例如,重新加载单元格或表,只需更新cell在顶部返回的现有对象的图像属性tableView:cellForRowAtIndexPath(如果该行已滚动离开屏幕并且该单元格已经出队并被重新用于新行,则是一个问题),等等)。通过使用cellForRowAtIndexPath(不要与混淆tableView:cellForRowAtIndexPath),您可以确定该单元格是否仍然可见和/或它是否已经滚动下来,是否已出队并可以重复使用。

最简单的解决方案是使用经过压力测试的频繁使用的产品。

SDWebImage是一个功能强大的工具,可以帮助我解决类似的问题,可以轻松地通过可可豆荚进行安装。在podfile中:

platform :ios, '6.1'
pod 'SDWebImage', '~>3.6'

设置缓存:

SDImageCache *imageCache = [[SDImageCache alloc] initWithNamespace:@"myNamespace"];
[imageCache queryDiskCacheForKey:myCacheKey done:^(UIImage *image)
{
    // image is not nil if image was found
}];

快取图片:

[[SDImageCache sharedImageCache] storeImage:myImage forKey:myCacheKey];

https://github.com/rs/SDWebImage

我认为对您来说,像DLImageLoader这样的用户会更好。更多信息-> https://github.com/AndreyLunevich/DLImageLoader-iOS

[[DLImageLoader sharedInstance] loadImageFromUrl:@"image_url_here"
                                   completed:^(NSError *error, UIImage *image) {
                                        if (error == nil) {
                                            imageView.image = image;
                                        } else {
                                            // if we got an error when load an image
                                        }
                                   }];

对于有关错误图像的问题,这部分是由于单元的重用。重用单元格意味着看不见的现有单元格(例如,当您向底部滚动时,从顶部屏幕移出屏幕的单元格是从底部再次返回的单元格。)图片不正确。但是,一旦该单元格显示出来,就会执行用于获取正确图像的代码,您将获得正确的图像。

您可以在单元格的“ prepareForReuse”方法中使用占位符。当您需要在单元格重新使用时需要重置值时,通常使用此功能。在此处设置占位符将确保您不会得到任何不正确的图像。

Caching images can be done as simply as this.

ImageService.m

@implementation ImageService{
    NSCache * Cache;
}

const NSString * imageCacheKeyPrefix = @"Image-";

-(id) init {
    self = [super init];
    if(self) {
        Cache = [[NSCache alloc] init];
    }
    return self;
}

/** 
 * Get Image from cache first and if not then get from server
 * 
 **/

- (void) getImage: (NSString *) key
        imagePath: (NSString *) imagePath
       completion: (void (^)(UIImage * image)) handler
    {
    UIImage * image = [Cache objectForKey: key];

    if( ! image || imagePath == nil || ! [imagePath length])
    {
        image = NOIMAGE;  // Macro (UIImage*) for no image 

        [Cache setObject:image forKey: key];

        dispatch_async(dispatch_get_main_queue(), ^(void){
            handler(image);
        });
    }
    else
    {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0ul ),^(void){

            UIImage * image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[imagePath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]];

            if( !image)
            {
                image = NOIMAGE;
            }

            [Cache setObject:image forKey: key];

            dispatch_async(dispatch_get_main_queue(), ^(void){
                handler(image);
            });
        });
    }
}


- (void) getUserImage: (NSString *) userId
           completion: (void (^)(UIImage * image)) handler
{
    [self getImage: [NSString stringWithFormat: @"%@user-%@", imageCacheKeyPrefix, userId]
         imagePath: [NSString stringWithFormat: @"http://graph.facebook.com/%@/picture?type=square", userId]
        completion: handler];
}

SomeViewController.m

    [imageService getUserImage: userId
                    completion: ^(UIImage *image) {
            annotationImage.image = image;
    }];
////.h file

#import <UIKit/UIKit.h>

@interface UIImageView (KJ_Imageview_WebCache)


-(void)loadImageUsingUrlString :(NSString *)urlString placeholder :(UIImage *)placeholder_image;

@end

//.m file


#import "UIImageView+KJ_Imageview_WebCache.h"


@implementation UIImageView (KJ_Imageview_WebCache)




-(void)loadImageUsingUrlString :(NSString *)urlString placeholder :(UIImage *)placeholder_image
{




    NSString *imageUrlString = urlString;
    NSURL *url = [NSURL URLWithString:urlString];



    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,     NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *getImagePath = [documentsDirectory stringByAppendingPathComponent:[self tream_char:urlString]];

    NSLog(@"getImagePath--->%@",getImagePath);

    UIImage *customImage = [UIImage imageWithContentsOfFile:getImagePath];




    if (customImage)
    {
        self.image = customImage;


        return;
    }
    else
    {
         self.image=placeholder_image;
    }


    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *uploadTask = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {


        if (error)
        {
            NSLog(@"%@",[error localizedDescription]);

           self.image=placeholder_image;

            return ;
        }

        dispatch_async(dispatch_get_main_queue(), ^{


            UIImage *imageToCache = [UIImage imageWithData:data];



            if (imageUrlString == urlString)
            {

                self.image = imageToCache;
            }



            [self saveImage:data ImageString:[self tream_char:urlString]];

        });




    }];

    [uploadTask resume];



}


-(NSString *)tream_char :(NSString *)string
{
    NSString *unfilteredString =string;

    NSCharacterSet *notAllowedChars = [[NSCharacterSet characterSetWithCharactersInString:@"!@#$%^&*()_+|abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"] invertedSet];
    NSString *resultString = [[unfilteredString componentsSeparatedByCharactersInSet:notAllowedChars] componentsJoinedByString:@""];
    NSLog (@"Result: %@", resultString);



    return resultString;

}

-(void)saveImage : (NSData *)Imagedata ImageString : (NSString *)imageString
{

    NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);

    NSString* documentDirectory = [documentDirectories objectAtIndex:0];
    NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:imageString];




    if (![Imagedata writeToFile:documentDirectoryFilename atomically:NO])
    {
        NSLog((@"Failed to cache image data to disk"));
    }
    else
    {
        NSLog(@"the cachedImagedPath is %@",documentDirectoryFilename);
    }

}

@end


/// call 

 [cell.ProductImage loadImageUsingUrlString:[[ArrProductList objectAtIndex:indexPath.row] valueForKey:@"product_image"] placeholder:[UIImage imageNamed:@"app_placeholder"]];
本文地址:http://ios.askforanswer.com/zaiiosyingyongchengxushanghuancuntuxiangdezuijiafangfa.html
文章标签: ,   ,   ,   ,  
版权声明:本文为原创文章,版权归 admin 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!