UIImageView+XHURLDownload.m 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. //
  2. // UIImageView+XHURLDownload.m
  3. // XHImageViewer
  4. //
  5. // Created by 曾 宪华 on 14-2-18.
  6. // Copyright (c) 2014年 曾宪华 开发团队(http://iyilunba.com ) 本人QQ:543413507 本人QQ群(142557668). All rights reserved.
  7. //
  8. #import "UIImageView+XHURLDownload.h"
  9. #import <objc/runtime.h>
  10. #import "XHCacheManager.h"
  11. const char* const kXHURLPropertyKey = "XHURLDownloadURLPropertyKey";
  12. const char* const kXHLoadingStateKey = "XHURLDownloadLoadingStateKey";
  13. const char* const kXHLoadingViewKey = "XHURLDownloadLoadingViewKey";
  14. const char* const kXHActivityIndicatorViewKey = "XHActivityIndicatorViewKey";
  15. #define kXHActivityIndicatorViewSize 35
  16. @implementation UIImageView (XHURLDownload)
  17. + (id)imageViewWithURL:(NSURL *)url autoLoading:(BOOL)autoLoading {
  18. UIImageView *view = [self new];
  19. view.url = url;
  20. if(autoLoading) {
  21. [view load];
  22. }
  23. return view;
  24. }
  25. + (id)indicatorImageView {
  26. UIImageView *view = [self new];
  27. [view setDefaultLoadingView];
  28. return view;
  29. }
  30. + (id)indicatorImageViewWithURL:(NSURL *)url autoLoading:(BOOL)autoLoading {
  31. UIImageView *view = [self imageViewWithURL:url autoLoading:autoLoading];
  32. [view setDefaultLoadingView];
  33. return view;
  34. }
  35. #pragma mark- Properties
  36. - (dispatch_queue_t)cachingQueue {
  37. static dispatch_queue_t cachingQeueu;
  38. static dispatch_once_t onceToken;
  39. dispatch_once(&onceToken, ^{
  40. cachingQeueu = dispatch_queue_create("caching image and data", NULL);
  41. });
  42. return cachingQeueu;
  43. }
  44. - (void)setActivityIndicatorView:(UIActivityIndicatorView *)activityIndicatorView {
  45. objc_setAssociatedObject(self, kXHActivityIndicatorViewKey, activityIndicatorView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  46. }
  47. - (UIActivityIndicatorView *)activityIndicatorView {
  48. return objc_getAssociatedObject(self, kXHActivityIndicatorViewKey);
  49. }
  50. - (NSURL*)url {
  51. return objc_getAssociatedObject(self, kXHURLPropertyKey);
  52. }
  53. - (void)setUrl:(NSURL *)url {
  54. [self setUrl:url autoLoading:NO];
  55. }
  56. - (void)setUrl:(NSURL *)url autoLoading:(BOOL)autoLoading {
  57. if(![url isEqual:self.url]) {
  58. objc_setAssociatedObject(self, kXHURLPropertyKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  59. if (url) {
  60. self.loadingState = UIImageViewURLDownloadStateWaitingForLoad;
  61. }
  62. else {
  63. self.loadingState = UIImageViewURLDownloadStateUnknown;
  64. }
  65. }
  66. if(autoLoading) {
  67. [self load];
  68. }
  69. }
  70. - (void)loadWithURL:(NSURL *)url {
  71. [self loadWithURL:url placeholer:nil];
  72. }
  73. - (void)loadWithURL:(NSURL *)url placeholer:(UIImage *)placeholerImage {
  74. [self loadWithURL:url placeholer:placeholerImage showActivityIndicatorView:NO];
  75. }
  76. - (void)loadWithURL:(NSURL *)url placeholer:(UIImage *)placeholerImage showActivityIndicatorView:(BOOL)show {
  77. [self _setupPlaecholerImage:placeholerImage showActivityIndicatorView:show];
  78. [self setUrl:url autoLoading:YES];
  79. }
  80. - (void)loadWithURL:(NSURL *)url placeholer:(UIImage *)placeholerImage showActivityIndicatorView:(BOOL)show completionBlock:(void(^)(UIImage *image, NSURL *url, NSError *error))handler {
  81. [self _setupPlaecholerImage:placeholerImage showActivityIndicatorView:show];
  82. [self setUrl:url autoLoading:NO];
  83. [self loadWithCompletionBlock:handler];
  84. }
  85. - (void)_setupPlaecholerImage:(UIImage *)placeholerImage showActivityIndicatorView:(BOOL)show {
  86. if (placeholerImage)
  87. [self setImage:placeholerImage];
  88. if (show) {
  89. UIActivityIndicatorView *activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
  90. activityIndicatorView.frame = CGRectMake(0, 0, kXHActivityIndicatorViewSize, kXHActivityIndicatorViewSize);
  91. activityIndicatorView.center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
  92. [activityIndicatorView startAnimating];
  93. [self addSubview:activityIndicatorView];
  94. [self setActivityIndicatorView:activityIndicatorView];
  95. }
  96. }
  97. - (UIImageViewURLDownloadState)loadingState {
  98. return (NSUInteger)([objc_getAssociatedObject(self, kXHLoadingStateKey) integerValue]);
  99. }
  100. - (void)setLoadingState:(UIImageViewURLDownloadState)loadingState {
  101. objc_setAssociatedObject(self, kXHLoadingStateKey, @(loadingState), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  102. }
  103. - (UIView *)loadingView {
  104. return objc_getAssociatedObject(self, kXHLoadingViewKey);
  105. }
  106. - (void)setLoadingView:(UIView *)loadingView {
  107. [self.loadingView removeFromSuperview];
  108. objc_setAssociatedObject(self, kXHLoadingViewKey, loadingView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  109. loadingView.center = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2);
  110. loadingView.alpha = 0;
  111. [self addSubview:loadingView];
  112. }
  113. - (void)setDefaultLoadingView {
  114. UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
  115. indicator.frame = self.frame;
  116. indicator.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  117. indicator.backgroundColor = [UIColor colorWithWhite:0.9 alpha:1];
  118. self.loadingView = indicator;
  119. }
  120. #pragma mark- Loading view
  121. - (void)showLoadingView {
  122. dispatch_async(dispatch_get_main_queue(), ^{
  123. self.loadingView.alpha = 1;
  124. if([self.loadingView respondsToSelector:@selector(startAnimating)]) {
  125. [self.loadingView performSelector:@selector(startAnimating)];
  126. }
  127. });
  128. }
  129. - (void)hideLoadingView {
  130. dispatch_async(dispatch_get_main_queue(), ^{
  131. UIActivityIndicatorView *activityIndicatorView = [self activityIndicatorView];
  132. if (activityIndicatorView) {
  133. [activityIndicatorView stopAnimating];
  134. [activityIndicatorView removeFromSuperview];
  135. }
  136. [UIView animateWithDuration:0.3
  137. animations:^{
  138. self.loadingView.alpha = 0;
  139. }
  140. completion:^(BOOL finished) {
  141. if([self.loadingView respondsToSelector:@selector(stopAnimating)]) {
  142. [self.loadingView performSelector:@selector(stopAnimating)];
  143. }
  144. }
  145. ];
  146. });
  147. }
  148. #pragma mark- Image downloading
  149. + (NSOperationQueue *)downloadQueue {
  150. static NSOperationQueue *_sharedQueue = nil;
  151. if(_sharedQueue == nil) {
  152. _sharedQueue = [NSOperationQueue new];
  153. [_sharedQueue setMaxConcurrentOperationCount:3];
  154. }
  155. return _sharedQueue;
  156. }
  157. + (void)dataWithContentsOfURL:(NSURL *)url completionBlock:(void (^)(NSURL *, NSData *, NSError *))completion {
  158. NSURLRequest *request = [NSURLRequest requestWithURL:url];
  159. NSURLSession *session = [NSURLSession sharedSession];
  160. NSURLSessionDataTask *task = [session dataTaskWithRequest:request
  161. completionHandler:
  162. ^(NSData *data, NSURLResponse *response, NSError *error) {
  163. if(completion) {
  164. completion(url, data, error);
  165. }
  166. }];
  167. [task resume];
  168. /*
  169. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
  170. [request setHTTPMethod:@"GET"];
  171. [request setTimeoutInterval:5.0];
  172. [NSURLConnection sendAsynchronousRequest:request
  173. queue:[self downloadQueue]
  174. completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
  175. if(completion) {
  176. completion(url, data, connectionError);
  177. }
  178. }
  179. ];
  180. */
  181. }
  182. - (void)load {
  183. [self loadWithCompletionBlock:nil];
  184. }
  185. - (void)loadWithCompletionBlock:(void(^)(UIImage *image, NSURL *url, NSError *error))handler {
  186. self.loadingState = UIImageViewURLDownloadStateNowLoading;
  187. [self showLoadingView];
  188. __weak typeof(self) weakSelf = self;
  189. dispatch_async(self.cachingQueue, ^{
  190. UIImage *cacheImage = [XHCacheManager imageWithURL:weakSelf.url storeMemoryCache:YES];
  191. if (cacheImage) {
  192. [self setImage:cacheImage forURL:weakSelf.url];
  193. if (handler)
  194. handler(cacheImage, weakSelf.url, nil);
  195. } else {
  196. // It could be more better by replacing with a method that has delegates like a progress.
  197. [UIImageView dataWithContentsOfURL:weakSelf.url
  198. completionBlock:^(NSURL *url, NSData *data, NSError *error) {
  199. UIImage *image = [weakSelf didFinishDownloadWithData:data forURL:url error:error];
  200. if(handler) {
  201. handler(image, url, error);
  202. }
  203. }
  204. ];
  205. }
  206. });
  207. }
  208. - (void)cachingImageData:(NSData *)imageData url:(NSURL *)url {
  209. dispatch_async(self.cachingQueue, ^{
  210. if (imageData) {
  211. [XHCacheManager storeData:imageData forURL:url storeMemoryCache:NO];
  212. UIImage *image = [UIImage imageWithData:imageData];
  213. if (image)
  214. [XHCacheManager storeMemoryCacheWithImage:image forURL:url];
  215. }
  216. });
  217. }
  218. - (UIImage *)didFinishDownloadWithData:(NSData *)data forURL:(NSURL *)url error:(NSError *)error {
  219. if (data) {
  220. [self cachingImageData:data url:url];
  221. }
  222. UIImage *image = [UIImage imageWithData:data];
  223. if([url isEqual:self.url]) {
  224. if(error) {
  225. self.loadingState = UIImageViewURLDownloadStateFailed;
  226. }
  227. else {
  228. [self performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
  229. self.loadingState = UIImageViewURLDownloadStateLoaded;
  230. }
  231. [self hideLoadingView];
  232. }
  233. return image;
  234. }
  235. - (void)setImage:(UIImage *)image forURL:(NSURL *)url {
  236. if([url isEqual:self.url]) {
  237. [self performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
  238. self.loadingState = UIImageViewURLDownloadStateLoaded;
  239. [self hideLoadingView];
  240. }
  241. }
  242. @end