From 1c695d5388d4e1ee0a8f8d9f285a71940d10f677 Mon Sep 17 00:00:00 2001 From: Liming Song Date: Mon, 20 Oct 2014 22:01:15 +0800 Subject: [PATCH 1/4] added functions Load from cache only, click to download and click to refresh --- UIImageView+JMImageCache.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UIImageView+JMImageCache.m b/UIImageView+JMImageCache.m index 3a3502a..a0053ae 100644 --- a/UIImageView+JMImageCache.m +++ b/UIImageView+JMImageCache.m @@ -53,7 +53,7 @@ - (void) setImageWithURL:(NSURL *)url key:(NSString*)key placeholder:(UIImage *) [self setImageWithURL:url key:key placeholder:placeholderImage completionBlock:nil]; } - (void) setImageWithURL:(NSURL *)url key:(NSString*)key placeholder:(UIImage *)placeholderImage completionBlock:(void (^)(UIImage *image))completionBlock { - [self setImageWithURL:url key:key placeholder:placeholderImage completionBlock:completionBlock]; + [self setImageWithURL:url key:key placeholder:placeholderImage completionBlock:completionBlock failureBlock:nil]; } - (void) setImageWithURL:(NSURL *)url key:(NSString*)key placeholder:(UIImage *)placeholderImage completionBlock:(void (^)(UIImage *image))completionBlock failureBlock:(void (^)(NSURLRequest *request, NSURLResponse *response, NSError* error))failureBlock{ self.jm_imageURL = url; From 340890ef9804d8ab47a644329f7835903944fce9 Mon Sep 17 00:00:00 2001 From: Liming Song Date: Sun, 26 Oct 2014 23:58:34 +0800 Subject: [PATCH 2/4] simplified and optimized by deleting some unused methods and combining them; by adding options to clickToDownload and clickToRefresh image. --- Demo/Classes/DemoViewController.m | 64 +++---- .../project.pbxproj | 2 + JMImageCache.h | 31 +--- JMImageCache.m | 166 +++++++----------- UIImageView+JMImageCache.h | 16 +- UIImageView+JMImageCache.m | 155 ++++++++++++++-- 6 files changed, 260 insertions(+), 174 deletions(-) diff --git a/Demo/Classes/DemoViewController.m b/Demo/Classes/DemoViewController.m index 04bc4d6..e5c6f39 100644 --- a/Demo/Classes/DemoViewController.m +++ b/Demo/Classes/DemoViewController.m @@ -11,76 +11,80 @@ @interface DemoViewController () -@property (strong, nonatomic) NSMutableArray *modelArray; +@property (copy, nonatomic) NSArray *modelArray; @end @implementation DemoViewController +static NSString *CellIdentifier = @"Cell"; // cell identifier + @synthesize modelArray = _modelArray; -- (id) init { +- (instancetype) init { self = [super init]; if(!self) return nil; self.title = @"The Office"; - self.modelArray = [[NSMutableArray alloc] init]; - - [self.modelArray addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"http://cl.ly/4hCR/Untitled-7.png", @"ImageURL", @"Michael Scott", @"Title", nil]]; - [self.modelArray addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"http://cl.ly/4iIc/Untitled-7.png", @"ImageURL", @"Jim Halpert", @"Title", nil]]; - [self.modelArray addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"http://cl.ly/4hVv/Untitled-7.png", @"ImageURL", @"Pam Beasley-Halpert", @"Title", nil]]; - [self.modelArray addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"http://cl.ly/4hF3/Untitled-7.png", @"ImageURL", @"Dwight Schrute", @"Title", nil]]; - [self.modelArray addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"http://cl.ly/4hxj/Untitled-7.png", @"ImageURL", @"Andy Bernard", @"Title", nil]]; - [self.modelArray addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"http://cl.ly/4iNI/Untitled-7.png", @"ImageURL", @"Kevin Malone", @"Title", nil]]; - [self.modelArray addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"http://cl.ly/4iAX/Untitled-7.png", @"ImageURL", @"Stanley Hudson", @"Title", nil]]; + self.modelArray = @[@{@"ImageURL" : @"http://cl.ly/4hCR/Untitled-7.png", @"Title" : @"Michael Scott"}, + @{@"ImageURL" : @"http://cl.ly/4iIc/Untitled-7.png", @"Title" : @"Jim Halpert"}, + @{@"ImageURL" : @"http://cl.ly/4hVv/Untitled-7.png", @"Title" : @"Pam Beasley-Halpert"}, + @{@"ImageURL" : @"http://cl.ly/4hF3/Untitled-7.png", @"Title" : @"Dwight Schrute"}, + @{@"ImageURL" : @"http://cl.ly/4hxj/Untitled-7.png", @"Title" : @"Andy Bernard"}, + @{@"ImageURL" : @"http://cl.ly/4iNI/Untitled-7.png", @"Title" : @"Kevin Malone"}, + @{@"ImageURL" : @"http://cl.ly/4iAX/Untitled-7.png", @"Title" : @"Stanley Hudson"}]; // You should remove this next line from your apps!!! // It is only here for demonstration purposes, so you can get an idea for what it's like to load images "fresh" for the first time. [[JMImageCache sharedCache] removeAllObjects]; + [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:CellIdentifier]; + return self; } -#pragma mark - -#pragma mark Autorotation Methods +#pragma mark - Autorotation Methods - (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation { return toInterfaceOrientation != UIInterfaceOrientationPortraitUpsideDown; } -#pragma mark - -#pragma mark Cleanup Methods +#pragma mark - Cleanup Methods - (void) didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } -#pragma mark - -#pragma mark UITableViewDataSource +#pragma mark - UITableViewDataSource - (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.modelArray count]; } - (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - static NSString *CellIdentifier = @"Cell"; - - UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; - if(cell == nil) cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; - - NSString *urlString = [[self.modelArray objectAtIndex:indexPath.row] objectForKey:@"ImageURL"]; - - cell.textLabel.text = [[self.modelArray objectAtIndex:indexPath.row] objectForKey:@"Title"]; - - [cell.imageView setImageWithURL:[NSURL URLWithString:urlString] - placeholder:[UIImage imageNamed:@"placeholder"]]; + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; + + NSDictionary *cellDic = (self.modelArray)[indexPath.row]; + cell.textLabel.text = cellDic[@"Title"]; + + NSURL *url = [NSURL URLWithString:cellDic[@"ImageURL"]]; + JMImageCacheDownloadOptions option = JMImageCacheDownloadOptionsSearchCacheOnly | JMImageCacheDownloadOptionsClickToDownload; + option |= JMImageCacheDownloadOptionsClickToRefresh; + [cell.imageView setImageWithURL:url + key:nil + options:option + placeholder:[UIImage imageNamed:@"placeholder"] + completionBlock:^(UIImage *image) { + // + } failureBlock:^(NSURLRequest *request, NSURLResponse *response, NSError *error) { + // + }]; return cell; } -#pragma mark - -#pragma mark UITableViewDelegate +#pragma mark - UITableViewDelegate - (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return 90.0; diff --git a/Demo/JMImageCacheDemo.xcodeproj/project.pbxproj b/Demo/JMImageCacheDemo.xcodeproj/project.pbxproj index 593613c..5ee70bd 100755 --- a/Demo/JMImageCacheDemo.xcodeproj/project.pbxproj +++ b/Demo/JMImageCacheDemo.xcodeproj/project.pbxproj @@ -225,6 +225,7 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ENABLE_OBJC_ARC = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; @@ -241,6 +242,7 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ENABLE_OBJC_ARC = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Other Sources/JMImageCacheDemo_Prefix.pch"; diff --git a/JMImageCache.h b/JMImageCache.h index f527da0..201aea6 100644 --- a/JMImageCache.h +++ b/JMImageCache.h @@ -8,38 +8,23 @@ #import "UIImageView+JMImageCache.h" -@class JMImageCache; - -@protocol JMImageCacheDelegate - -@optional -- (void) cache:(JMImageCache *)c didDownloadImage:(UIImage *)i forURL:(NSURL *)url; -- (void) cache:(JMImageCache *)c didDownloadImage:(UIImage *)i forURL:(NSURL *)url key:(NSString*)key; - -@end - @interface JMImageCache : NSCache + (JMImageCache *) sharedCache; -- (void) imageForURL:(NSURL *)url key:(NSString *)key completionBlock:(void (^)(UIImage *image))completion failureBlock:(void (^)(NSURLRequest *request, NSURLResponse *response, NSError* error))failure; -- (void) imageForURL:(NSURL *)url completionBlock:(void (^)(UIImage *image))completion failureBlock:(void (^)(NSURLRequest *request, NSURLResponse *response, NSError* error))failure; - -- (UIImage *) cachedImageForKey:(NSString *)key; -- (UIImage *) cachedImageForURL:(NSURL *)url; +- (void) imageForURL:(NSURL *)url key:(NSString *)key completionBlock:(JMICCompletionBlock)completion failureBlock:(JMICFailureBlock)failure; +- (void) imageForURL:(NSURL *)url completionBlock:(JMICCompletionBlock)completion failureBlock:(JMICFailureBlock)failure; -- (UIImage *) imageForURL:(NSURL *)url key:(NSString*)key delegate:(id)d; -- (UIImage *) imageForURL:(NSURL *)url delegate:(id)d; +- (UIImage *) cachedImageForURL:(NSURL *)url key:(NSString *)key;; -- (UIImage *) imageFromDiskForKey:(NSString *)key; -- (UIImage *) imageFromDiskForURL:(NSURL *)url; +- (UIImage *) imageFromDiskForURL:(NSURL *)url key:(NSString *)key;; -- (void) setImage:(UIImage *)i forKey:(NSString *)key; -- (void) setImage:(UIImage *)i forURL:(NSURL *)url; -- (void) removeImageForKey:(NSString *)key; -- (void) removeImageForURL:(NSURL *)url; +- (void) setImage:(UIImage *)i forURL:(NSURL *)url key:(NSString *)key; +- (void) removeImageForURL:(NSURL *)url key:(NSString *)key; - (void) writeData:(NSData *)data toPath:(NSString *)path; - (void) performDiskWriteOperation:(NSInvocation *)invoction; +- (void) _downloadAndWriteImageForURL:(NSURL *)url key:(NSString *)key completionBlock:(JMICCompletionBlock)completion failureBlock:(JMICFailureBlock)failure; + @end diff --git a/JMImageCache.m b/JMImageCache.m index 007958e..057ecb8 100644 --- a/JMImageCache.m +++ b/JMImageCache.m @@ -30,8 +30,6 @@ @interface JMImageCache () @property (strong, nonatomic) NSOperationQueue *diskOperationQueue; -- (void) _downloadAndWriteImageForURL:(NSURL *)url key:(NSString *)key completionBlock:(void (^)(UIImage *image))completion failureBlock:(void (^)(NSURLRequest *request, NSURLResponse *response, NSError* error))failure; - @end @implementation JMImageCache @@ -39,21 +37,21 @@ @implementation JMImageCache @synthesize diskOperationQueue = _diskOperationQueue; + (JMImageCache *) sharedCache { - static JMImageCache *_sharedCache = nil; + static JMImageCache *sCache = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - _sharedCache = [[JMImageCache alloc] init]; + sCache = [[JMImageCache alloc] init]; }); - return _sharedCache; + return sCache; } -- (id) init { +- (instancetype) init { self = [super init]; if(!self) return nil; - self.diskOperationQueue = [[NSOperationQueue alloc] init]; + _diskOperationQueue = [[NSOperationQueue alloc] init]; [[NSFileManager defaultManager] createDirectoryAtPath:JMImageCacheDirectory() withIntermediateDirectories:YES @@ -62,43 +60,39 @@ - (id) init { return self; } -- (void) _downloadAndWriteImageForURL:(NSURL *)url key:(NSString *)key completionBlock:(void (^)(UIImage *image))completion failureBlock:(void (^)(NSURLRequest *request, NSURLResponse *response, NSError* error))failure +- (void) _downloadAndWriteImageForURL:(NSURL *)url key:(NSString *)key completionBlock:(JMICCompletionBlock)completion failureBlock:(JMICFailureBlock)failure { - if (!key && !url) return; - if (!key) { + if (!url) return; key = keyForURL(url); } dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - NSURLRequest* request = [NSURLRequest requestWithURL:url]; NSURLResponse* response = nil; NSError* error = nil; NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; - if (error) - { - dispatch_async(dispatch_get_main_queue(), ^{ - - if(failure) failure(request, response, error); - }); + if (error) { + if(failure) { + dispatch_async(dispatch_get_main_queue(), ^{ + failure(request, response, error); + }); + } return; } UIImage *i = [[UIImage alloc] initWithData:data]; - if (!i) - { + if (!i) { NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary]; [errorDetail setValue:[NSString stringWithFormat:@"Failed to init image with data from for URL: %@", url] forKey:NSLocalizedDescriptionKey]; NSError* error = [NSError errorWithDomain:@"JMImageCacheErrorDomain" code:1 userInfo:errorDetail]; - dispatch_async(dispatch_get_main_queue(), ^{ - - if(failure) failure(request, response, error); - }); - } - else - { + if(failure) { + dispatch_async(dispatch_get_main_queue(), ^{ + failure(request, response, error); + }); + } + } else { NSString *cachePath = cachePathForKey(key); NSInvocation *writeInvocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(writeData:toPath:)]]; @@ -108,11 +102,13 @@ - (void) _downloadAndWriteImageForURL:(NSURL *)url key:(NSString *)key completio [writeInvocation setArgument:&cachePath atIndex:3]; [self performDiskWriteOperation:writeInvocation]; - [self setImage:i forKey:key]; + [self setImage:i forURL:url key:key]; - dispatch_async(dispatch_get_main_queue(), ^{ - if(completion) completion(i); - }); + if(completion) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(i); + }); + } } }); } @@ -139,6 +135,7 @@ - (void) removeAllObjects { } }); } + - (void) removeObjectForKey:(id)key { [super removeObjectForKey:key]; @@ -155,12 +152,18 @@ - (void) removeObjectForKey:(id)key { }); } -#pragma mark - -#pragma mark Getter Methods +- (void) removeImageForURL:(NSURL *)url key:(NSString *)key { + if (key == nil) { + key = keyForURL(url); + } + [self removeObjectForKey:key]; +} + +#pragma mark - Getter Methods -- (void) imageForURL:(NSURL *)url key:(NSString *)key completionBlock:(void (^)(UIImage *image))completion failureBlock:(void (^)(NSURLRequest *request, NSURLResponse *response, NSError* error))failure{ +- (void) imageForURL:(NSURL *)url key:(NSString *)key completionBlock:(JMICCompletionBlock)completion failureBlock:(JMICFailureBlock)failure { - UIImage *i = [self cachedImageForKey:key]; + UIImage *i = [self cachedImageForURL:url key:key]; if(i) { if(completion) completion(i); @@ -169,89 +172,44 @@ - (void) imageForURL:(NSURL *)url key:(NSString *)key completionBlock:(void (^)( } } -- (void) imageForURL:(NSURL *)url completionBlock:(void (^)(UIImage *image))completion failureBlock:(void (^)(NSURLRequest *request, NSURLResponse *response, NSError* error))failure{ +- (void) imageForURL:(NSURL *)url completionBlock:(JMICCompletionBlock)completion failureBlock:(JMICFailureBlock)failure { [self imageForURL:url key:keyForURL(url) completionBlock:completion failureBlock:(failure)]; } -- (UIImage *) cachedImageForKey:(NSString *)key { - if(!key) return nil; - - id returner = [super objectForKey:key]; - - if(returner) { - return returner; - } else { - UIImage *i = [self imageFromDiskForKey:key]; - if(i) [self setImage:i forKey:key]; - - return i; +- (UIImage *) cachedImageForURL:(NSURL *)url key:(NSString *)key { + if (key == nil) { + key = keyForURL(url); } + if (key == nil) return nil; - return nil; -} - -- (UIImage *) cachedImageForURL:(NSURL *)url { - NSString *key = keyForURL(url); - return [self cachedImageForKey:key]; -} - -- (UIImage *) imageForURL:(NSURL *)url key:(NSString*)key delegate:(id)d { - if(!url) return nil; - - UIImage *i = [self cachedImageForURL:url]; - - if(i) { - return i; - } else { - [self _downloadAndWriteImageForURL:url key:key completionBlock:^(UIImage *image) { - if(d) { - if([d respondsToSelector:@selector(cache:didDownloadImage:forURL:)]) { - [d cache:self didDownloadImage:image forURL:url]; - } - if([d respondsToSelector:@selector(cache:didDownloadImage:forURL:key:)]) { - [d cache:self didDownloadImage:image forURL:url key:key]; - } - } - } - failureBlock:nil]; + UIImage *i = [super objectForKey:key]; + if (i == nil) { + i = [self imageFromDiskForURL:url key:key]; + [self setImage:i forURL:url key:key]; } - - return nil; -} - -- (UIImage *) imageForURL:(NSURL *)url delegate:(id)d { - return [self imageForURL:url key:keyForURL(url) delegate:d]; + return i; } -- (UIImage *) imageFromDiskForKey:(NSString *)key { - UIImage *i = [[UIImage alloc] initWithData:[NSData dataWithContentsOfFile:cachePathForKey(key) options:0 error:NULL]]; - return i; -} - -- (UIImage *) imageFromDiskForURL:(NSURL *)url { - return [self imageFromDiskForKey:keyForURL(url)]; +- (UIImage *) imageFromDiskForURL:(NSURL *)url key:(NSString *)key { + if (key == nil) { + key = keyForURL(url); + } + NSData *imgData = [NSData dataWithContentsOfFile:cachePathForKey(key) options:0 error:NULL]; + return [[UIImage alloc] initWithData:imgData]; } -#pragma mark - -#pragma mark Setter Methods +#pragma mark - Setter Methods -- (void) setImage:(UIImage *)i forKey:(NSString *)key { - if (i) { - [super setObject:i forKey:key]; - } -} -- (void) setImage:(UIImage *)i forURL:(NSURL *)url { - [self setImage:i forKey:keyForURL(url)]; -} -- (void) removeImageForKey:(NSString *)key { - [self removeObjectForKey:key]; -} -- (void) removeImageForURL:(NSURL *)url { - [self removeImageForKey:keyForURL(url)]; +- (void) setImage:(UIImage *)i forURL:(NSURL *)url key:(NSString *)key { + if (key == nil) { + key = keyForURL(url); + } + if (key != nil && i != nil) { + [super setObject:i forKey:key]; + } } -#pragma mark - -#pragma mark Disk Writing Operations +#pragma mark - Disk Writing Operations - (void) writeData:(NSData*)data toPath:(NSString *)path { [data writeToFile:path atomically:YES]; diff --git a/UIImageView+JMImageCache.h b/UIImageView+JMImageCache.h index e294bc7..419b7ee 100644 --- a/UIImageView+JMImageCache.h +++ b/UIImageView+JMImageCache.h @@ -8,15 +8,27 @@ #import + +typedef NS_OPTIONS(NSUInteger, JMImageCacheDownloadOptions) { + JMImageCacheDownloadOptionsNone, + JMImageCacheDownloadOptionsSearchCacheOnly = 1UL, + JMImageCacheDownloadOptionsClickToDownload = 2UL, + JMImageCacheDownloadOptionsClickToRefresh = 4UL +}; + +typedef void (^JMICCompletionBlock)(UIImage *image); +typedef void (^JMICFailureBlock)(NSURLRequest *request, NSURLResponse *response, NSError *error); + @interface UIImageView (JMImageCache) - (void) setImageWithURL:(NSURL *)url; - (void) setImageWithURL:(NSURL *)url placeholder:(UIImage *)placeholderImage; - (void) setImageWithURL:(NSURL *)url placeholder:(UIImage *)placeholderImage completionBlock:(void (^)(UIImage *))completionBlock; -- (void) setImageWithURL:(NSURL *)url placeholder:(UIImage *)placeholderImage completionBlock:(void (^)(UIImage *))completionBlock failureBlock:(void (^)(NSURLRequest *request, NSURLResponse *response, NSError* error))failure; +- (void) setImageWithURL:(NSURL *)url placeholder:(UIImage *)placeholderImage completionBlock:(void (^)(UIImage *))completionBlock failureBlock:(JMICFailureBlock)failure; - (void) setImageWithURL:(NSURL *)url key:(NSString*)key placeholder:(UIImage *)placeholderImage; - (void) setImageWithURL:(NSURL *)url key:(NSString*)key placeholder:(UIImage *)placeholderImage completionBlock:(void (^)(UIImage *image))completionBlock; -- (void) setImageWithURL:(NSURL *)url key:(NSString*)key placeholder:(UIImage *)placeholderImage completionBlock:(void (^)(UIImage *image))completionBlock failureBlock:(void (^)(NSURLRequest *request, NSURLResponse *response, NSError* error))failure; +- (void) setImageWithURL:(NSURL *)url key:(NSString*)key placeholder:(UIImage *)placeholderImage completionBlock:(void (^)(UIImage *image))completionBlock failureBlock:(JMICFailureBlock)failure; +- (void) setImageWithURL:(NSURL *)url key:(NSString*)key options:(JMImageCacheDownloadOptions)options placeholder:(UIImage *)placeholderImage completionBlock:(void (^)(UIImage *image))completionBlock failureBlock:(JMICFailureBlock)failureBlock; @end \ No newline at end of file diff --git a/UIImageView+JMImageCache.m b/UIImageView+JMImageCache.m index a0053ae..e49a40a 100644 --- a/UIImageView+JMImageCache.m +++ b/UIImageView+JMImageCache.m @@ -11,20 +11,27 @@ #import static char kJMImageURLObjectKey; +static char kTapToDownloadGesture; +static char kKey; +static char kOptions; +static char kPlaceholderImage; +static char kCompletionBlock; +static char kFailureBlock; @interface UIImageView (_JMImageCache) -@property (readwrite, nonatomic, retain, setter = jm_setImageURL:) NSURL *jm_imageURL; +@property (readwrite, nonatomic, strong, setter = jm_setImageURL:) NSURL *jm_imageURL; +@property (nonatomic, strong)UITapGestureRecognizer *jm_tapGestureRecognizer; -@end - -@implementation UIImageView (_JMImageCache) - -@dynamic jm_imageURL; +@property (nonatomic, copy)NSString *jm_key; +@property (nonatomic)JMImageCacheDownloadOptions jm_options; +@property (nonatomic, strong)UIImage *jm_placeholderImage; +@property (nonatomic, copy)void (^jm_completionBlock)(UIImage *image); +@property (nonatomic, copy)void (^jm_failureBlock)(NSURLRequest *request, NSURLResponse *response, NSError *error); @end -@implementation UIImageView (JMImageCache) +@implementation UIImageView (_JMImageCache) #pragma mark - Private Setters @@ -35,6 +42,60 @@ - (void) jm_setImageURL:(NSURL *)imageURL { objc_setAssociatedObject(self, &kJMImageURLObjectKey, imageURL, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } +- (UITapGestureRecognizer *) jm_tapGestureRecognizer { + UITapGestureRecognizer *t = (UITapGestureRecognizer *)objc_getAssociatedObject(self, &kTapToDownloadGesture); + if (t == nil) { + t = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapToDownload:)]; + self.jm_tapGestureRecognizer = t; + } + return t; +} +- (void) setJm_tapGestureRecognizer:(UITapGestureRecognizer *)tap { + objc_setAssociatedObject(self, &kTapToDownloadGesture, tap, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSString *)jm_key { + return (NSString *)objc_getAssociatedObject(self, &kKey); +} +- (void)setJm_key:(NSString *)jm_key { + objc_setAssociatedObject(self, &kKey, jm_key, OBJC_ASSOCIATION_COPY_NONATOMIC); +} + +- (JMImageCacheDownloadOptions)jm_options { + return (JMImageCacheDownloadOptions)[objc_getAssociatedObject(self, &kOptions) unsignedIntegerValue]; +} +- (void)setJm_options:(JMImageCacheDownloadOptions)jm_options { + objc_setAssociatedObject(self, &kOptions, @(jm_options), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (UIImage *)jm_placeholderImage { + return (UIImage *)objc_getAssociatedObject(self, &kPlaceholderImage); +} +- (void)setJm_placeholderImage:(UIImage *)jm_placeholderImage { + objc_setAssociatedObject(self, &kPlaceholderImage, jm_placeholderImage, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (void (^)(UIImage *))jm_completionBlock { + return (void (^)(UIImage *))objc_getAssociatedObject(self, &kCompletionBlock); +} +- (void)setJm_completionBlock:(void (^)(UIImage *))jm_completionBlock { + objc_setAssociatedObject(self, &kCompletionBlock, jm_completionBlock, OBJC_ASSOCIATION_COPY_NONATOMIC); +} + +- (void (^)(NSURLRequest *, NSURLResponse *, NSError *))jm_failureBlock { + return (void (^)(NSURLRequest *, NSURLResponse *, NSError *))objc_getAssociatedObject(self, &kFailureBlock); +} +- (void)setJm_failureBlock:(void (^)(NSURLRequest *, NSURLResponse *, NSError *))jm_failureBlock { + objc_setAssociatedObject(self, &kFailureBlock, jm_failureBlock, OBJC_ASSOCIATION_COPY_NONATOMIC); +} + +@end + +@implementation UIImageView (JMImageCache) + + +#pragma mark - Private Methods + #pragma mark - Public Methods - (void) setImageWithURL:(NSURL *)url { @@ -65,14 +126,7 @@ - (void) setImageWithURL:(NSURL *)url key:(NSString*)key placeholder:(UIImage *) __weak UIImageView *safeSelf = self; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - UIImage *i; - - if (key) { - i = [[JMImageCache sharedCache] cachedImageForKey:key]; - } else { - i = [[JMImageCache sharedCache] cachedImageForURL:url]; - } - + UIImage *i = [[JMImageCache sharedCache] cachedImageForURL:url key:key]; if(i) { dispatch_async(dispatch_get_main_queue(), ^{ safeSelf.jm_imageURL = nil; @@ -116,4 +170,75 @@ - (void) setImageWithURL:(NSURL *)url key:(NSString*)key placeholder:(UIImage *) }); } + +- (void) setImageWithURL:(NSURL *)url key:(NSString*)key options:(JMImageCacheDownloadOptions)options placeholder:(UIImage *)placeholderImage completionBlock:(void (^)(UIImage *image))completionBlock failureBlock:(void (^)(NSURLRequest *request, NSURLResponse *response, NSError* error))failureBlock { + + // 有ClickToDownload 或 ClickToRefresh 时,才添加手势,并保留参数 + if ((options & JMImageCacheDownloadOptionsClickToDownload) != 0 + || (options & JMImageCacheDownloadOptionsClickToRefresh) != 0) { + if (![self.gestureRecognizers containsObject:self.jm_tapGestureRecognizer]) { + self.userInteractionEnabled = YES; + [self addGestureRecognizer:self.jm_tapGestureRecognizer]; + } + + // 保留参数。 + self.jm_imageURL = url; + self.jm_key = key; + self.jm_options = options & ~JMImageCacheDownloadOptionsSearchCacheOnly; // 去除只读cache属性 + self.jm_placeholderImage = placeholderImage; + self.jm_completionBlock = completionBlock; + self.jm_failureBlock = failureBlock; + } + + UIImage *i = [[JMImageCache sharedCache] imageFromDiskForURL:url key:key]; + + self.image = i ? i : placeholderImage; + if (options & JMImageCacheDownloadOptionsSearchCacheOnly) { + return; // 结束 + } + // 下载图片 + UIActivityIndicatorView *ai = (UIActivityIndicatorView *)[self viewWithTag:[self hash]]; + if (ai == nil) { + ai = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; + CGSize size = self.frame.size; + ai.center = (CGPoint){size.width / 2, size.height / 2}; + ai.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin; + self.autoresizesSubviews = YES; + [self addSubview:ai]; + } + [ai startAnimating]; + [[JMImageCache sharedCache] _downloadAndWriteImageForURL:url key:key completionBlock:^(UIImage *image) { + self.image = image; + [ai stopAnimating]; + if (options & JMImageCacheDownloadOptionsClickToRefresh) { + // + } else if (options & JMImageCacheDownloadOptionsClickToDownload) { + [self removeGestureRecognizer:self.jm_tapGestureRecognizer]; + } + if (completionBlock) { + completionBlock(image); + } + } failureBlock:^(NSURLRequest *request, NSURLResponse *response, NSError *error) { + self.image = i ? i : placeholderImage; + [ai stopAnimating]; + if (options & JMImageCacheDownloadOptionsClickToRefresh) { + // + } else if (options & JMImageCacheDownloadOptionsClickToDownload) { + [self removeGestureRecognizer:self.jm_tapGestureRecognizer]; + } + if (failureBlock) { + failureBlock(request, response, error); + } + }]; +} + +- (void)tapToDownload:(id)sender { + [self setImageWithURL:self.jm_imageURL + key:self.jm_key + options:self.jm_options + placeholder:self.jm_placeholderImage + completionBlock:self.jm_completionBlock + failureBlock:self.jm_failureBlock]; +} + @end \ No newline at end of file From ba952446714ae8d0bed57b7d72c65e4985ad9116 Mon Sep 17 00:00:00 2001 From: Liming Song Date: Mon, 27 Oct 2014 22:51:45 +0800 Subject: [PATCH 3/4] nearly marvelous logic --- Demo/Classes/DemoViewController.m | 8 +-- README.markdown | 14 +++++- UIImageView+JMImageCache.h | 1 + UIImageView+JMImageCache.m | 83 ++++++++++--------------------- 4 files changed, 42 insertions(+), 64 deletions(-) diff --git a/Demo/Classes/DemoViewController.m b/Demo/Classes/DemoViewController.m index e5c6f39..4cce7d8 100644 --- a/Demo/Classes/DemoViewController.m +++ b/Demo/Classes/DemoViewController.m @@ -68,10 +68,10 @@ - (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:( NSDictionary *cellDic = (self.modelArray)[indexPath.row]; cell.textLabel.text = cellDic[@"Title"]; - NSURL *url = [NSURL URLWithString:cellDic[@"ImageURL"]]; - JMImageCacheDownloadOptions option = JMImageCacheDownloadOptionsSearchCacheOnly | JMImageCacheDownloadOptionsClickToDownload; - option |= JMImageCacheDownloadOptionsClickToRefresh; - [cell.imageView setImageWithURL:url + JMImageCacheDownloadOptions option = JMImageCacheDownloadOptionsClickToDownload; +// option |= JMImageCacheDownloadOptionsClickToRefresh; + option |= JMImageCacheDownloadOptionsSearchCacheOnly; + [cell.imageView setImageWithURL:[NSURL URLWithString:cellDic[@"ImageURL"]] key:nil options:option placeholder:[UIImage imageNamed:@"placeholder"] diff --git a/README.markdown b/README.markdown index 55d3137..3a0f4f9 100644 --- a/README.markdown +++ b/README.markdown @@ -29,8 +29,18 @@ The idea behind `JMImageCache` is to always return images the **fastest** way po The clean and easy way (uses a category that `JMImageCache` adds to `UIImageView`): ``` objective-c -[cell.imageView setImageWithURL:[NSURL URLWithString:@"http://dundermifflin.com/i/MichaelScott.png"] - placeholder:[UIImage imageNamed:@"placeholder.png"]]; + JMImageCacheDownloadOptions option = JMImageCacheDownloadOptionsClickToDownload; + // option |= JMImageCacheDownloadOptionsClickToRefresh; + option |= JMImageCacheDownloadOptionsSearchCacheOnly; + [cell.imageView setImageWithURL:[NSURL URLWithString:@"http://dundermifflin.com/i/MichaelScott.png"] + key:nil + options:option + placeholder:[UIImage imageNamed:@"placeholder"] + completionBlock:^(UIImage *image) { + // + } failureBlock:^(NSURLRequest *request, NSURLResponse *response, NSError *error) { + // + }]; ``` Request an image like so: diff --git a/UIImageView+JMImageCache.h b/UIImageView+JMImageCache.h index 419b7ee..19da405 100644 --- a/UIImageView+JMImageCache.h +++ b/UIImageView+JMImageCache.h @@ -29,6 +29,7 @@ typedef void (^JMICFailureBlock)(NSURLRequest *request, NSURLResponse *response, - (void) setImageWithURL:(NSURL *)url key:(NSString*)key placeholder:(UIImage *)placeholderImage completionBlock:(void (^)(UIImage *image))completionBlock; - (void) setImageWithURL:(NSURL *)url key:(NSString*)key placeholder:(UIImage *)placeholderImage completionBlock:(void (^)(UIImage *image))completionBlock failureBlock:(JMICFailureBlock)failure; +/// key优先,无key时使用url;key无法用于从网上获取图片,仅能用于读缓存 - (void) setImageWithURL:(NSURL *)url key:(NSString*)key options:(JMImageCacheDownloadOptions)options placeholder:(UIImage *)placeholderImage completionBlock:(void (^)(UIImage *image))completionBlock failureBlock:(JMICFailureBlock)failureBlock; @end \ No newline at end of file diff --git a/UIImageView+JMImageCache.m b/UIImageView+JMImageCache.m index e49a40a..dece377 100644 --- a/UIImageView+JMImageCache.m +++ b/UIImageView+JMImageCache.m @@ -116,58 +116,8 @@ - (void) setImageWithURL:(NSURL *)url key:(NSString*)key placeholder:(UIImage *) - (void) setImageWithURL:(NSURL *)url key:(NSString*)key placeholder:(UIImage *)placeholderImage completionBlock:(void (^)(UIImage *image))completionBlock { [self setImageWithURL:url key:key placeholder:placeholderImage completionBlock:completionBlock failureBlock:nil]; } -- (void) setImageWithURL:(NSURL *)url key:(NSString*)key placeholder:(UIImage *)placeholderImage completionBlock:(void (^)(UIImage *image))completionBlock failureBlock:(void (^)(NSURLRequest *request, NSURLResponse *response, NSError* error))failureBlock{ - self.jm_imageURL = url; - self.image = placeholderImage; - - [self setNeedsDisplay]; - [self setNeedsLayout]; - - __weak UIImageView *safeSelf = self; - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - UIImage *i = [[JMImageCache sharedCache] cachedImageForURL:url key:key]; - if(i) { - dispatch_async(dispatch_get_main_queue(), ^{ - safeSelf.jm_imageURL = nil; - - safeSelf.image = i; - - [safeSelf setNeedsLayout]; - [safeSelf setNeedsDisplay]; - }); - } else { - dispatch_async(dispatch_get_main_queue(), ^{ - safeSelf.image = placeholderImage; - - [safeSelf setNeedsDisplay]; - [safeSelf setNeedsLayout]; - }); - - [[JMImageCache sharedCache] imageForURL:url key:key completionBlock:^(UIImage *image) { - if ([url isEqual:safeSelf.jm_imageURL]) { - dispatch_async(dispatch_get_main_queue(), ^{ - if(image) { - safeSelf.image = image; - } else { - safeSelf.image = placeholderImage; - } - - safeSelf.jm_imageURL = nil; - - [safeSelf setNeedsLayout]; - [safeSelf setNeedsDisplay]; - - if (completionBlock) completionBlock(image); - }); - } - } - failureBlock:^(NSURLRequest *request, NSURLResponse *response, NSError* error) - { - if (failureBlock) failureBlock(request, response, error); - }]; - } - }); +- (void) setImageWithURL:(NSURL *)url key:(NSString*)key placeholder:(UIImage *)placeholderImage completionBlock:(JMICCompletionBlock)completionBlock failureBlock:(JMICFailureBlock)failureBlock { + [self setImageWithURL:url key:key options:JMImageCacheDownloadOptionsNone placeholder:placeholderImage completionBlock:nil failureBlock:nil]; } @@ -193,9 +143,30 @@ - (void) setImageWithURL:(NSURL *)url key:(NSString*)key options:(JMImageCacheDo UIImage *i = [[JMImageCache sharedCache] imageFromDiskForURL:url key:key]; self.image = i ? i : placeholderImage; - if (options & JMImageCacheDownloadOptionsSearchCacheOnly) { + + // cacheOnly 优先,其次才是clickToRefresh,最次是clickToDownload + if (0 != (options & JMImageCacheDownloadOptionsSearchCacheOnly)) { + if (i) { + if (completionBlock) { + completionBlock(i); + } + } else { + if (failureBlock) { + NSError *err = [NSError errorWithDomain:@"JMImageCacheError" code:1 userInfo:@{NSLocalizedDescriptionKey : @"cache里未找到你要的图片"}]; + failureBlock(nil, nil, err); + } + } return; // 结束 } + + if (0 != (options & JMImageCacheDownloadOptionsClickToRefresh)) { + // continue to download + } else if (i) { + if (completionBlock) { + completionBlock(i); + } + return; + } // 下载图片 UIActivityIndicatorView *ai = (UIActivityIndicatorView *)[self viewWithTag:[self hash]]; if (ai == nil) { @@ -221,11 +192,7 @@ - (void) setImageWithURL:(NSURL *)url key:(NSString*)key options:(JMImageCacheDo } failureBlock:^(NSURLRequest *request, NSURLResponse *response, NSError *error) { self.image = i ? i : placeholderImage; [ai stopAnimating]; - if (options & JMImageCacheDownloadOptionsClickToRefresh) { - // - } else if (options & JMImageCacheDownloadOptionsClickToDownload) { - [self removeGestureRecognizer:self.jm_tapGestureRecognizer]; - } + if (failureBlock) { failureBlock(request, response, error); } From b71b227135194208285fb8dabbe797341ec67147 Mon Sep 17 00:00:00 2001 From: Liming Song Date: Sat, 22 Nov 2014 12:08:21 +0800 Subject: [PATCH 4/4] add index.html --- index.html | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 index.html diff --git a/index.html b/index.html new file mode 100644 index 0000000..f010409 --- /dev/null +++ b/index.html @@ -0,0 +1,7 @@ + + + +

Hello World

+

I'm hosted with GitHub Pages.

+ + \ No newline at end of file