Ver Fonte

Refactored all the duplicate code from our WebCache categories into a UIView+WebCache category. All the other categories will make calls to this one. Customization of setting the image is done via the setImageBlock and the operationKey

Bogdan Poplauschi há 9 anos atrás
pai
commit
e1840c3

+ 1 - 0
Examples/SDWebImage Demo/MasterViewController.m

@@ -11,6 +11,7 @@
 #import "DetailViewController.h"
 #import <SDWebImage/FLAnimatedImageView.h>
 #import <SDWebImage/FLAnimatedImageView+WebCache.h>
+#import <SDWebImage/UIView+WebCache.h>
 
 
 @interface MyCustomTableViewCell : UITableViewCell

+ 28 - 0
SDWebImage.xcodeproj/project.pbxproj

@@ -297,6 +297,18 @@
 		431BB6F91D06D2C1006A3455 /* UIImage+GIF.h in Headers */ = {isa = PBXBuildFile; fileRef = A18A6CC5172DC28500419892 /* UIImage+GIF.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		431BB6FA1D06D2C1006A3455 /* SDWebImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D8B148C56230056699D /* SDWebImageDownloader.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		431BB6FC1D06D2C1006A3455 /* SDWebImageDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D89148C56230056699D /* SDWebImageDecoder.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		4369C2771D9807EC007E863A /* UIView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 4369C2751D9807EC007E863A /* UIView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		4369C2781D9807EC007E863A /* UIView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 4369C2751D9807EC007E863A /* UIView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		4369C2791D9807EC007E863A /* UIView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 4369C2751D9807EC007E863A /* UIView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		4369C27A1D9807EC007E863A /* UIView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 4369C2751D9807EC007E863A /* UIView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		4369C27B1D9807EC007E863A /* UIView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 4369C2751D9807EC007E863A /* UIView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		4369C27C1D9807EC007E863A /* UIView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 4369C2751D9807EC007E863A /* UIView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		4369C27E1D9807EC007E863A /* UIView+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 4369C2761D9807EC007E863A /* UIView+WebCache.m */; };
+		4369C27F1D9807EC007E863A /* UIView+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 4369C2761D9807EC007E863A /* UIView+WebCache.m */; };
+		4369C2801D9807EC007E863A /* UIView+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 4369C2761D9807EC007E863A /* UIView+WebCache.m */; };
+		4369C2811D9807EC007E863A /* UIView+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 4369C2761D9807EC007E863A /* UIView+WebCache.m */; };
+		4369C2821D9807EC007E863A /* UIView+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 4369C2761D9807EC007E863A /* UIView+WebCache.m */; };
+		4369C2831D9807EC007E863A /* UIView+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 4369C2761D9807EC007E863A /* UIView+WebCache.m */; };
 		438096721CDFC08200DC626B /* MKAnnotationView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 535699B415113E7300A4C397 /* MKAnnotationView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		438096731CDFC08F00DC626B /* MKAnnotationView+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 535699B515113E7300A4C397 /* MKAnnotationView+WebCache.m */; };
 		438096741CDFC09C00DC626B /* UIImage+WebP.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFB911762547C00698166 /* UIImage+WebP.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -891,6 +903,8 @@
 		00733A4C1BC487C000A5A117 /* SDWebImage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SDWebImage.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		4314D1991D0E0E3B004B36C9 /* libSDWebImage watchOS static.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libSDWebImage watchOS static.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		431BB7031D06D2C1006A3455 /* SDWebImage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SDWebImage.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		4369C2751D9807EC007E863A /* UIView+WebCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIView+WebCache.h"; path = "SDWebImage/UIView+WebCache.h"; sourceTree = "<group>"; };
+		4369C2761D9807EC007E863A /* UIView+WebCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIView+WebCache.m"; path = "SDWebImage/UIView+WebCache.m"; sourceTree = "<group>"; };
 		4397D2F21D0DDD8C00BB2784 /* SDWebImage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SDWebImage.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		4397D2F41D0DE2DF00BB2784 /* NSImage+WebCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSImage+WebCache.h"; path = "SDWebImage/NSImage+WebCache.h"; sourceTree = "<group>"; };
 		4397D2F51D0DE2DF00BB2784 /* NSImage+WebCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSImage+WebCache.m"; path = "SDWebImage/NSImage+WebCache.m"; sourceTree = "<group>"; };
@@ -1116,6 +1130,8 @@
 				ABBE71A618C43B4D00B75E91 /* UIImageView+HighlightedWebCache.m */,
 				53922D95148C56230056699D /* UIImageView+WebCache.h */,
 				53922D96148C56230056699D /* UIImageView+WebCache.m */,
+				4369C2751D9807EC007E863A /* UIView+WebCache.h */,
+				4369C2761D9807EC007E863A /* UIView+WebCache.m */,
 			);
 			name = "WebCache Categories";
 			path = ..;
@@ -1438,6 +1454,7 @@
 				431739561CDFC8B70008FEB9 /* demux.h in Headers */,
 				4317394D1CDFC8B20008FEB9 /* utils.h in Headers */,
 				4397D2F81D0DF44200BB2784 /* MKAnnotationView+WebCache.h in Headers */,
+				4369C27A1D9807EC007E863A /* UIView+WebCache.h in Headers */,
 				431739451CDFC8B20008FEB9 /* quant_levels_dec.h in Headers */,
 				431739361CDFC8B20008FEB9 /* bit_reader_inl.h in Headers */,
 				4317393A1CDFC8B20008FEB9 /* color_cache.h in Headers */,
@@ -1524,6 +1541,7 @@
 				4314D17B1D0E0E3B004B36C9 /* huffman_encode.h in Headers */,
 				43DA7CD91D10865E0028BE58 /* dsp.h in Headers */,
 				4314D17C1D0E0E3B004B36C9 /* UIImage+WebP.h in Headers */,
+				4369C2781D9807EC007E863A /* UIView+WebCache.h in Headers */,
 				4314D17D1D0E0E3B004B36C9 /* SDWebImagePrefetcher.h in Headers */,
 				4314D17E1D0E0E3B004B36C9 /* bit_writer.h in Headers */,
 				43DA7CF01D10865E0028BE58 /* neon.h in Headers */,
@@ -1585,6 +1603,7 @@
 				43DA7D931D1086600028BE58 /* lossless.h in Headers */,
 				431BB6EE1D06D2C1006A3455 /* NSData+ImageContentType.h in Headers */,
 				431BB6EF1D06D2C1006A3455 /* SDWebImagePrefetcher.h in Headers */,
+				4369C27B1D9807EC007E863A /* UIView+WebCache.h in Headers */,
 				431BB6F01D06D2C1006A3455 /* SDWebImageOperation.h in Headers */,
 				43A62A201D0E0A800089D7DD /* mux_types.h in Headers */,
 				43A62A2F1D0E0A860089D7DD /* huffman_encode.h in Headers */,
@@ -1621,6 +1640,7 @@
 				4397D2C31D0DDD8C00BB2784 /* SDWebImageManager.h in Headers */,
 				4397D2C41D0DDD8C00BB2784 /* SDImageCache.h in Headers */,
 				4397D2C51D0DDD8C00BB2784 /* UIImageView+WebCache.h in Headers */,
+				4369C27C1D9807EC007E863A /* UIView+WebCache.h in Headers */,
 				4397D2C61D0DDD8C00BB2784 /* random.h in Headers */,
 				4397D2C71D0DDD8C00BB2784 /* rescaler.h in Headers */,
 				43DA7DE11D10867D0028BE58 /* extras.h in Headers */,
@@ -1675,6 +1695,7 @@
 				431739211CDFC8B20008FEB9 /* endian_inl.h in Headers */,
 				43CE757D1CFE9427006C64D0 /* FLAnimatedImageView.h in Headers */,
 				431739541CDFC8B70008FEB9 /* types.h in Headers */,
+				4369C2791D9807EC007E863A /* UIView+WebCache.h in Headers */,
 				431738C51CDFC8A30008FEB9 /* alphai.h in Headers */,
 				4A2CAE041AB4BB5400B6BC39 /* SDWebImage.h in Headers */,
 				431739511CDFC8B70008FEB9 /* format_constants.h in Headers */,
@@ -1758,6 +1779,7 @@
 				431738BD1CDFC2660008FEB9 /* decode.h in Headers */,
 				53761319155AD0D5005750A4 /* SDWebImageDecoder.h in Headers */,
 				5376131A155AD0D5005750A4 /* SDWebImageDownloader.h in Headers */,
+				4369C2771D9807EC007E863A /* UIView+WebCache.h in Headers */,
 				5376131C155AD0D5005750A4 /* SDWebImageManager.h in Headers */,
 				431738B01CDFC2630008FEB9 /* huffman_encode.h in Headers */,
 				438096741CDFC09C00DC626B /* UIImage+WebP.h in Headers */,
@@ -2092,6 +2114,7 @@
 				43DA7D681D1086600028BE58 /* yuv_mips_dsp_r2.c in Sources */,
 				43DA7D651D1086600028BE58 /* upsampling_neon.c in Sources */,
 				43DA7D371D1086600028BE58 /* alpha_processing.c in Sources */,
+				4369C2811D9807EC007E863A /* UIView+WebCache.m in Sources */,
 				43DA7D691D1086600028BE58 /* yuv_mips32.c in Sources */,
 				43DA7D5F1D1086600028BE58 /* rescaler_mips_dsp_r2.c in Sources */,
 				00733A5E1BC4880000A5A117 /* UIImage+MultiFormat.m in Sources */,
@@ -2123,6 +2146,7 @@
 				43DA7CDB1D10865E0028BE58 /* enc_mips_dsp_r2.c in Sources */,
 				4314D12F1D0E0E3B004B36C9 /* thread.c in Sources */,
 				4314D1311D0E0E3B004B36C9 /* SDWebImageDownloader.m in Sources */,
+				4369C27F1D9807EC007E863A /* UIView+WebCache.m in Sources */,
 				43DA7CC91D10865E0028BE58 /* alpha_processing.c in Sources */,
 				4314D1321D0E0E3B004B36C9 /* filters.c in Sources */,
 				4314D1341D0E0E3B004B36C9 /* UIImage+WebP.m in Sources */,
@@ -2220,6 +2244,7 @@
 				43DA7D801D1086600028BE58 /* enc_mips_dsp_r2.c in Sources */,
 				43A62A631D0E0A8F0089D7DD /* vp8l.c in Sources */,
 				431BB6A31D06D2C1006A3455 /* UIImageView+WebCache.m in Sources */,
+				4369C2821D9807EC007E863A /* UIView+WebCache.m in Sources */,
 				43DA7D6E1D1086600028BE58 /* alpha_processing.c in Sources */,
 				43A62A5E1D0E0A8F0089D7DD /* io.c in Sources */,
 				43A62A321D0E0A860089D7DD /* quant_levels_dec.c in Sources */,
@@ -2375,6 +2400,7 @@
 				43DA7DB01D1086610028BE58 /* dec_mips32.c in Sources */,
 				43DA7DAA1D1086610028BE58 /* cost_mips32.c in Sources */,
 				43DA7DAB1D1086610028BE58 /* cost_sse2.c in Sources */,
+				4369C2831D9807EC007E863A /* UIView+WebCache.m in Sources */,
 				43DA7DB71D1086610028BE58 /* enc_mips_dsp_r2.c in Sources */,
 				43DA7DC11D1086610028BE58 /* lossless_enc_mips32.c in Sources */,
 				43DA7DBC1D1086610028BE58 /* enc.c in Sources */,
@@ -2486,6 +2512,7 @@
 				43DA7D311D10865F0028BE58 /* yuv_mips_dsp_r2.c in Sources */,
 				43DA7D2E1D10865F0028BE58 /* upsampling_neon.c in Sources */,
 				43DA7D001D10865F0028BE58 /* alpha_processing.c in Sources */,
+				4369C2801D9807EC007E863A /* UIView+WebCache.m in Sources */,
 				43DA7D321D10865F0028BE58 /* yuv_mips32.c in Sources */,
 				43DA7D281D10865F0028BE58 /* rescaler_mips_dsp_r2.c in Sources */,
 				431738C81CDFC8A30008FEB9 /* frame.c in Sources */,
@@ -2587,6 +2614,7 @@
 				43DA7CC31D1086570028BE58 /* yuv_mips_dsp_r2.c in Sources */,
 				43DA7CC01D1086570028BE58 /* upsampling_neon.c in Sources */,
 				43DA7C921D1086570028BE58 /* alpha_processing.c in Sources */,
+				4369C27E1D9807EC007E863A /* UIView+WebCache.m in Sources */,
 				43DA7CC41D1086570028BE58 /* yuv_mips32.c in Sources */,
 				43DA7CBA1D1086570028BE58 /* rescaler_mips_dsp_r2.c in Sources */,
 				4317387C1CDFC2580008FEB9 /* frame.c in Sources */,

+ 2 - 15
SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.h

@@ -11,9 +11,9 @@
 #if SD_UIKIT
 
 #if COCOAPODS
-    @import FLAnimatedImage;
+@import FLAnimatedImage;
 #else
-    #import "FLAnimatedImageView.h"
+#import "FLAnimatedImageView.h"
 #endif
 
 #import "SDWebImageManager.h"
@@ -25,14 +25,6 @@
  */
 @interface FLAnimatedImageView (WebCache)
 
-/**
- * Get the current image URL.
- *
- * Note that because of the limitations of categories this property can get out of sync
- * if you use setImage: directly.
- */
-- (nullable NSURL *)sd_imageURL;
-
 /**
  * Load the image at the given url (either from cache or download) and load it in this imageView. It works with both static and dynamic images
  * The download is asynchronous and cached.
@@ -136,11 +128,6 @@
                   progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                  completed:(nullable SDExternalCompletionBlock)completedBlock;
 
-/**
- * Cancel the image load
- */
-- (void)sd_cancelCurrentImageLoad;
-
 @end
 
 #endif

+ 18 - 69
SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.m

@@ -11,19 +11,13 @@
 #import "FLAnimatedImageView+WebCache.h"
 #import "objc/runtime.h"
 #import "UIView+WebCacheOperation.h"
+#import "UIView+WebCache.h"
 #import "NSData+ImageContentType.h"
 #import "FLAnimatedImage.h"
 #import "UIImageView+WebCache.h"
 
-static char imageURLKey;
-
-
 @implementation FLAnimatedImageView (WebCache)
 
-- (nullable NSURL *)sd_imageURL {
-    return objc_getAssociatedObject(self, &imageURLKey);
-}
-
 - (void)sd_setImageWithURL:(nullable NSURL *)url {
     [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil];
 }
@@ -53,70 +47,25 @@ static char imageURLKey;
                    options:(SDWebImageOptions)options
                   progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                  completed:(nullable SDExternalCompletionBlock)completedBlock {
-    [self sd_cancelCurrentImageLoad];
-    objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
-
-    if (!(options & SDWebImageDelayPlaceholder)) {
-        dispatch_main_async_safe(^{
-            self.image = placeholder;
-        });
-    }
-    
-    if (url) {
-        // check if activityView is enabled or not
-        if ([self showActivityIndicatorView]) {
-            [self addActivityIndicator];
-        }
-        
-        __weak __typeof(self)wself = self;
-        id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
-            if (!wself) return;
-            dispatch_main_sync_safe(^{
-                [wself removeActivityIndicator];
-                
-                if (!wself) return;
-                if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) {
-                    completedBlock(image, error, cacheType, url);
-                    return;
-                } else if (image) {
-                    SDImageFormat imageFormat = [NSData sd_imageFormatForImageData:data];
-                    if (imageFormat == SDImageFormatGIF) {
-                        wself.animatedImage = [FLAnimatedImage animatedImageWithGIFData:data];
-                        wself.image = nil;
-                    } else {
-                        wself.image = image;
-                        wself.animatedImage = nil;
-                    }
-                    [wself setNeedsLayout];
-                } else {
-                    if ((options & SDWebImageDelayPlaceholder)) {
-                        wself.image = placeholder;
-                        [wself setNeedsLayout];
-                    }
-                }
-                if (completedBlock && finished) {
-                    completedBlock(image, error, cacheType, url);
-                }
-            });
-        }];
-        [self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"];
-    } else {
-        dispatch_main_async_safe(^{
-            [self removeActivityIndicator];
-            
-            if (completedBlock) {
-                NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
-                completedBlock(nil, error, SDImageCacheTypeNone, url);
-            }
-        });
-    }
-}
-
-- (void)sd_cancelCurrentImageLoad {
-    [self sd_cancelImageLoadOperationWithKey:@"UIImageViewImageLoad"];
+    __weak typeof(self)weakSelf = self;
+    [self sd_internalSetImageWithURL:url
+                    placeholderImage:placeholder
+                             options:options
+                        operationKey:nil
+                       setImageBlock:^(UIImage *image, NSData *imageData) {
+                           SDImageFormat imageFormat = [NSData sd_imageFormatForImageData:data];
+                           if (imageFormat == SDImageFormatGIF) {
+                               weakSelf.animatedImage = [FLAnimatedImage animatedImageWithGIFData:data];
+                               weakSelf.image = nil;
+                           } else {
+                               weakSelf.image = image;
+                               weakSelf.animatedImage = nil;
+                           }
+                       }
+                            progress:progressBlock
+                           completed:completedBlock];
 }
 
-
 @end
 
 #endif

+ 0 - 13
SDWebImage/MKAnnotationView+WebCache.h

@@ -18,14 +18,6 @@
  */
 @interface MKAnnotationView (WebCache)
 
-/**
- * Get the current image URL.
- *
- * Note that because of the limitations of categories this property can get out of sync
- * if you use setImage: directly.
- */
-- (nullable NSURL *)sd_imageURL;
-
 /**
  * Set the imageView `image` with an `url`.
  *
@@ -112,11 +104,6 @@
                    options:(SDWebImageOptions)options
                  completed:(nullable SDExternalCompletionBlock)completedBlock;
 
-/**
- * Cancel the current download
- */
-- (void)sd_cancelCurrentImageLoad;
-
 @end
 
 #endif

+ 11 - 56
SDWebImage/MKAnnotationView+WebCache.m

@@ -12,15 +12,10 @@
 
 #import "objc/runtime.h"
 #import "UIView+WebCacheOperation.h"
-
-static char imageURLKey;
+#import "UIView+WebCache.h"
 
 @implementation MKAnnotationView (WebCache)
 
-- (nullable NSURL *)sd_imageURL {
-    return objc_getAssociatedObject(self, &imageURLKey);
-}
-
 - (void)sd_setImageWithURL:(nullable NSURL *)url {
     [self sd_setImageWithURL:url placeholderImage:nil options:0 completed:nil];
 }
@@ -45,56 +40,16 @@ static char imageURLKey;
           placeholderImage:(nullable UIImage *)placeholder
                    options:(SDWebImageOptions)options
                  completed:(nullable SDExternalCompletionBlock)completedBlock {
-    [self sd_cancelCurrentImageLoad];
-
-    objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
-    self.image = placeholder;
-
-    if (url) {
-        __weak __typeof(self)wself = self;
-        id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:nil completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
-            if (!wself) return;
-            dispatch_main_sync_safe(^{
-                __strong MKAnnotationView *sself = wself;
-                if (!sself) return;
-                if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) {
-                    completedBlock(image, error, cacheType, url);
-                    return;
-                } else if (image) {
-                    wself.image = image;
-#if SD_UIKIT
-                    [wself setNeedsLayout];
-#elif SD_MAC
-                    [wself setNeedsLayout:YES];
-#endif
-                } else {
-                    if ((options & SDWebImageDelayPlaceholder)) {
-                        wself.image = placeholder;
-#if SD_UIKIT
-                        [wself setNeedsLayout];
-#elif SD_MAC
-                        [wself setNeedsDisplay:YES];
-#endif
-                    }
-                }
-                if (completedBlock && finished) {
-                    completedBlock(image, error, cacheType, url);
-                }
-            });
-        }];
-        [self sd_setImageLoadOperation:operation forKey:@"MKAnnotationViewImage"];
-    } else {
-        dispatch_main_async_safe(^{
-            NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
-            if (completedBlock) {
-                completedBlock(nil, error, SDImageCacheTypeNone, url);
-            }
-        });
-    }
-}
-
-- (void)sd_cancelCurrentImageLoad {
-    [self sd_cancelImageLoadOperationWithKey:@"MKAnnotationViewImage"];
+    __weak typeof(self)weakSelf = self;
+    [self sd_internalSetImageWithURL:url
+                    placeholderImage:placeholder
+                             options:options
+                        operationKey:nil
+                       setImageBlock:^(UIImage *image, NSData *imageData) {
+                           weakSelf.image = image;
+                       }
+                            progress:nil
+                           completed:completedBlock];
 }
 
 @end

+ 6 - 0
SDWebImage/UIButton+WebCache.h

@@ -22,6 +22,8 @@
  */
 - (nullable NSURL *)sd_currentImageURL;
 
+#pragma mark - Image
+
 /**
  * Get the image URL for a control state.
  * 
@@ -126,6 +128,8 @@
                    options:(SDWebImageOptions)options
                  completed:(nullable SDExternalCompletionBlock)completedBlock;
 
+#pragma mark - Background image
+
 /**
  * Set the backgroundImageView `image` with an `url`.
  *
@@ -222,6 +226,8 @@
                              options:(SDWebImageOptions)options
                            completed:(nullable SDExternalCompletionBlock)completedBlock;
 
+#pragma mark - Cancel
+
 /**
  * Cancel the current image download
  */

+ 33 - 66
SDWebImage/UIButton+WebCache.m

@@ -12,6 +12,7 @@
 
 #import "objc/runtime.h"
 #import "UIView+WebCacheOperation.h"
+#import "UIView+WebCache.h"
 
 static char imageURLStorageKey;
 
@@ -33,6 +34,8 @@ typedef NSMutableDictionary<NSNumber *, NSURL *> SDStateImageURLDictionary;
     return self.imageURLStorage[@(state)];
 }
 
+#pragma mark - Image
+
 - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state {
     [self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil];
 }
@@ -58,46 +61,27 @@ typedef NSMutableDictionary<NSNumber *, NSURL *> SDStateImageURLDictionary;
           placeholderImage:(nullable UIImage *)placeholder
                    options:(SDWebImageOptions)options
                  completed:(nullable SDExternalCompletionBlock)completedBlock {
-    [self setImage:placeholder forState:state];
-    [self sd_cancelImageLoadForState:state];
-    
     if (!url) {
         [self.imageURLStorage removeObjectForKey:@(state)];
-        
-        dispatch_main_async_safe(^{
-            if (completedBlock) {
-                NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
-                completedBlock(nil, error, SDImageCacheTypeNone, url);
-            }
-        });
-        
         return;
     }
     
     self.imageURLStorage[@(state)] = url;
-
-    __weak __typeof(self)wself = self;
-    id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:nil completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
-        if (!wself) return;
-        dispatch_main_sync_safe(^{
-            __strong UIButton *sself = wself;
-            if (!sself) return;
-            if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock)
-            {
-                completedBlock(image, error, cacheType, url);
-                return;
-            }
-            else if (image) {
-                [sself setImage:image forState:state];
-            }
-            if (completedBlock && finished) {
-                completedBlock(image, error, cacheType, url);
-            }
-        });
-    }];
-    [self sd_setImageLoadOperation:operation forState:state];
+    
+    __weak typeof(self)weakSelf = self;
+    [self sd_internalSetImageWithURL:url
+                    placeholderImage:placeholder
+                             options:options
+                        operationKey:[NSString stringWithFormat:@"UIButtonImageOperation%@", @(state)]
+                       setImageBlock:^(UIImage *image, NSData *imageData) {
+                           [weakSelf setImage:image forState:state];
+                       }
+                            progress:nil
+                           completed:completedBlock];
 }
 
+#pragma mark - Background image
+
 - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state {
     [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil];
 }
@@ -123,39 +107,23 @@ typedef NSMutableDictionary<NSNumber *, NSURL *> SDStateImageURLDictionary;
                     placeholderImage:(nullable UIImage *)placeholder
                              options:(SDWebImageOptions)options
                            completed:(nullable SDExternalCompletionBlock)completedBlock {
-    [self sd_cancelBackgroundImageLoadForState:state];
-
-    [self setBackgroundImage:placeholder forState:state];
-
-    if (url) {
-        __weak __typeof(self)wself = self;
-        id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:nil completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
-            if (!wself) return;
-            dispatch_main_sync_safe(^{
-                __strong UIButton *sself = wself;
-                if (!sself) return;
-                if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock)
-                {
-                    completedBlock(image, error, cacheType, url);
-                    return;
-                }
-                else if (image) {
-                    [sself setBackgroundImage:image forState:state];
-                }
-                if (completedBlock && finished) {
-                    completedBlock(image, error, cacheType, url);
-                }
-            });
-        }];
-        [self sd_setBackgroundImageLoadOperation:operation forState:state];
-    } else {
-        dispatch_main_async_safe(^{
-            NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
-            if (completedBlock) {
-                completedBlock(nil, error, SDImageCacheTypeNone, url);
-            }
-        });
+    if (!url) {
+        [self.imageURLStorage removeObjectForKey:@(state)];
+        return;
     }
+    
+    self.imageURLStorage[@(state)] = url;
+    
+    __weak typeof(self)weakSelf = self;
+    [self sd_internalSetImageWithURL:url
+                    placeholderImage:placeholder
+                             options:options
+                        operationKey:[NSString stringWithFormat:@"UIButtonBackgroundImageOperation%@", @(state)]
+                       setImageBlock:^(UIImage *image, NSData *imageData) {
+                           [weakSelf setBackgroundImage:image forState:state];
+                       }
+                            progress:nil
+                           completed:completedBlock];
 }
 
 - (void)sd_setImageLoadOperation:(id<SDWebImageOperation>)operation forState:(UIControlState)state {
@@ -176,8 +144,7 @@ typedef NSMutableDictionary<NSNumber *, NSURL *> SDStateImageURLDictionary;
 
 - (SDStateImageURLDictionary *)imageURLStorage {
     SDStateImageURLDictionary *storage = objc_getAssociatedObject(self, &imageURLStorageKey);
-    if (!storage)
-    {
+    if (!storage) {
         storage = [NSMutableDictionary dictionary];
         objc_setAssociatedObject(self, &imageURLStorageKey, storage, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
     }

+ 0 - 5
SDWebImage/UIImageView+HighlightedWebCache.h

@@ -88,11 +88,6 @@
                              progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                             completed:(nullable SDExternalCompletionBlock)completedBlock;
 
-/**
- * Cancel the current download
- */
-- (void)sd_cancelCurrentHighlightedImageLoad;
-
 @end
 
 #endif

+ 11 - 37
SDWebImage/UIImageView+HighlightedWebCache.m

@@ -11,8 +11,7 @@
 #if SD_UIKIT
 
 #import "UIView+WebCacheOperation.h"
-
-#define UIImageViewHighlightedWebCacheOperationKey @"highlightedImage"
+#import "UIView+WebCache.h"
 
 @implementation UIImageView (HighlightedWebCache)
 
@@ -36,41 +35,16 @@
                               options:(SDWebImageOptions)options
                              progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                             completed:(nullable SDExternalCompletionBlock)completedBlock {
-    [self sd_cancelCurrentHighlightedImageLoad];
-
-    if (url) {
-        __weak __typeof(self)wself = self;
-        id<SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
-            if (!wself) return;
-            dispatch_main_sync_safe (^{
-                if (!wself) return;
-                if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock)
-                {
-                    completedBlock(image, error, cacheType, url);
-                    return;
-                }
-                else if (image) {
-                    wself.highlightedImage = image;
-                    [wself setNeedsLayout];
-                }
-                if (completedBlock && finished) {
-                    completedBlock(image, error, cacheType, url);
-                }
-            });
-        }];
-        [self sd_setImageLoadOperation:operation forKey:UIImageViewHighlightedWebCacheOperationKey];
-    } else {
-        dispatch_main_async_safe(^{
-            NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
-            if (completedBlock) {
-                completedBlock(nil, error, SDImageCacheTypeNone, url);
-            }
-        });
-    }
-}
-
-- (void)sd_cancelCurrentHighlightedImageLoad {
-    [self sd_cancelImageLoadOperationWithKey:UIImageViewHighlightedWebCacheOperationKey];
+    __weak typeof(self)weakSelf = self;
+    [self sd_internalSetImageWithURL:url
+                    placeholderImage:nil
+                             options:options
+                        operationKey:@"UIImageViewImageOperationHighlighted"
+                       setImageBlock:^(UIImage *image, NSData *imageData) {
+                           weakSelf.highlightedImage = image;
+                       }
+                            progress:progressBlock
+                           completed:completedBlock];
 }
 
 @end

+ 3 - 30
SDWebImage/UIImageView+WebCache.h

@@ -47,14 +47,6 @@
  */
 @interface UIImageView (WebCache)
 
-/**
- * Get the current image URL.
- *
- * Note that because of the limitations of categories this property can get out of sync
- * if you use setImage: directly.
- */
-- (nullable NSURL *)sd_imageURL;
-
 /**
  * Set the imageView `image` with an `url`.
  *
@@ -183,37 +175,18 @@
                                         completed:(nullable SDExternalCompletionBlock)completedBlock;
 
 #if SD_UIKIT
+
+#pragma mark - Animation of multiple images
+
 /**
  * Download an array of images and starts them in an animation loop
  *
  * @param arrayOfURLs An array of NSURL
  */
 - (void)sd_setAnimationImagesWithURLs:(nonnull NSArray<NSURL *> *)arrayOfURLs;
-#endif
-
-/**
- * Cancel the current download
- */
-- (void)sd_cancelCurrentImageLoad;
 
-#if SD_UIKIT
 - (void)sd_cancelCurrentAnimationImagesLoad;
 
-/**
- *  Show activity UIActivityIndicatorView
- */
-- (void)setShowActivityIndicatorView:(BOOL)show;
-
-/**
- *  set desired UIActivityIndicatorViewStyle
- *
- *  @param style The style of the UIActivityIndicatorView
- */
-- (void)setIndicatorStyle:(UIActivityIndicatorViewStyle)style;
-
-- (BOOL)showActivityIndicatorView;
-- (void)addActivityIndicator;
-- (void)removeActivityIndicator;
 #endif
 
 @end

+ 11 - 146
SDWebImage/UIImageView+WebCache.m

@@ -12,13 +12,7 @@
 
 #import "objc/runtime.h"
 #import "UIView+WebCacheOperation.h"
-
-static char imageURLKey;
-#if SD_UIKIT
-static char TAG_ACTIVITY_INDICATOR;
-static char TAG_ACTIVITY_STYLE;
-#endif
-static char TAG_ACTIVITY_SHOW;
+#import "UIView+WebCache.h"
 
 @implementation UIImageView (WebCache)
 
@@ -51,65 +45,13 @@ static char TAG_ACTIVITY_SHOW;
                    options:(SDWebImageOptions)options
                   progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                  completed:(nullable SDExternalCompletionBlock)completedBlock {
-    [self sd_cancelCurrentImageLoad];
-    objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
-
-    if (!(options & SDWebImageDelayPlaceholder)) {
-        dispatch_main_async_safe(^{
-            self.image = placeholder;
-        });
-    }
-    
-    if (url) {
-
-        // check if activityView is enabled or not
-        if ([self showActivityIndicatorView]) {
-            [self addActivityIndicator];
-        }
-
-        __weak __typeof(self)wself = self;
-        id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
-            [wself removeActivityIndicator];
-            if (!wself) return;
-            dispatch_main_sync_safe(^{
-                if (!wself) return;
-                if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock)
-                {
-                    completedBlock(image, error, cacheType, url);
-                    return;
-                }
-                else if (image) {
-                    wself.image = image;
-#if SD_UIKIT
-                    [wself setNeedsLayout];
-#elif SD_MAC
-                    [wself setNeedsLayout:YES];
-#endif
-                } else {
-                    if ((options & SDWebImageDelayPlaceholder)) {
-                        wself.image = placeholder;
-#if SD_UIKIT
-                        [wself setNeedsLayout];
-#elif SD_MAC
-                        [wself setNeedsLayout:YES];
-#endif
-                    }
-                }
-                if (completedBlock && finished) {
-                    completedBlock(image, error, cacheType, url);
-                }
-            });
-        }];
-        [self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"];
-    } else {
-        dispatch_main_async_safe(^{
-            [self removeActivityIndicator];
-            if (completedBlock) {
-                NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
-                completedBlock(nil, error, SDImageCacheTypeNone, url);
-            }
-        });
-    }
+    [self sd_internalSetImageWithURL:url
+                    placeholderImage:placeholder
+                             options:options
+                        operationKey:nil
+                       setImageBlock:nil
+                            progress:progressBlock
+                           completed:completedBlock];
 }
 
 - (void)sd_setImageWithPreviousCachedImageWithURL:(nullable NSURL *)url
@@ -123,11 +65,10 @@ static char TAG_ACTIVITY_SHOW;
     [self sd_setImageWithURL:url placeholderImage:lastPreviousCachedImage ?: placeholder options:options progress:progressBlock completed:completedBlock];    
 }
 
-- (nullable NSURL *)sd_imageURL {
-    return objc_getAssociatedObject(self, &imageURLKey);
-}
-
 #if SD_UIKIT
+
+#pragma mark - Animation of multiple images
+
 - (void)sd_setAnimationImagesWithURLs:(nonnull NSArray<NSURL *> *)arrayOfURLs {
     [self sd_cancelCurrentAnimationImagesLoad];
     __weak __typeof(self)wself = self;
@@ -158,88 +99,12 @@ static char TAG_ACTIVITY_SHOW;
 
     [self sd_setImageLoadOperation:[operationsArray copy] forKey:@"UIImageViewAnimationImages"];
 }
-#endif
-
-- (void)sd_cancelCurrentImageLoad {
-    [self sd_cancelImageLoadOperationWithKey:@"UIImageViewImageLoad"];
-}
 
-#if SD_UIKIT
 - (void)sd_cancelCurrentAnimationImagesLoad {
     [self sd_cancelImageLoadOperationWithKey:@"UIImageViewAnimationImages"];
 }
 #endif
 
-#pragma mark -
-#if SD_UIKIT
-- (UIActivityIndicatorView *)activityIndicator {
-    return (UIActivityIndicatorView *)objc_getAssociatedObject(self, &TAG_ACTIVITY_INDICATOR);
-}
-
-- (void)setActivityIndicator:(UIActivityIndicatorView *)activityIndicator {
-    objc_setAssociatedObject(self, &TAG_ACTIVITY_INDICATOR, activityIndicator, OBJC_ASSOCIATION_RETAIN);
-}
-#endif
-
-- (void)setShowActivityIndicatorView:(BOOL)show {
-    objc_setAssociatedObject(self, &TAG_ACTIVITY_SHOW, @(show), OBJC_ASSOCIATION_RETAIN);
-}
-
-- (BOOL)showActivityIndicatorView {
-    return [objc_getAssociatedObject(self, &TAG_ACTIVITY_SHOW) boolValue];
-}
-
-#if SD_UIKIT
-- (void)setIndicatorStyle:(UIActivityIndicatorViewStyle)style{
-    objc_setAssociatedObject(self, &TAG_ACTIVITY_STYLE, [NSNumber numberWithInt:style], OBJC_ASSOCIATION_RETAIN);
-}
-
-- (int)getIndicatorStyle{
-    return [objc_getAssociatedObject(self, &TAG_ACTIVITY_STYLE) intValue];
-}
-#endif
-
-- (void)addActivityIndicator {
-#if SD_UIKIT
-    if (!self.activityIndicator) {
-        self.activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:[self getIndicatorStyle]];
-        self.activityIndicator.translatesAutoresizingMaskIntoConstraints = NO;
-
-        dispatch_main_async_safe(^{
-            [self addSubview:self.activityIndicator];
-
-            [self addConstraint:[NSLayoutConstraint constraintWithItem:self.activityIndicator
-                                                             attribute:NSLayoutAttributeCenterX
-                                                             relatedBy:NSLayoutRelationEqual
-                                                                toItem:self
-                                                             attribute:NSLayoutAttributeCenterX
-                                                            multiplier:1.0
-                                                              constant:0.0]];
-            [self addConstraint:[NSLayoutConstraint constraintWithItem:self.activityIndicator
-                                                             attribute:NSLayoutAttributeCenterY
-                                                             relatedBy:NSLayoutRelationEqual
-                                                                toItem:self
-                                                             attribute:NSLayoutAttributeCenterY
-                                                            multiplier:1.0
-                                                              constant:0.0]];
-        });
-    }
-
-    dispatch_main_async_safe(^{
-        [self.activityIndicator startAnimating];
-    });
-#endif
-}
-
-- (void)removeActivityIndicator {
-#if SD_UIKIT
-    if (self.activityIndicator) {
-        [self.activityIndicator removeFromSuperview];
-        self.activityIndicator = nil;
-    }
-#endif
-}
-
 @end
 
 #endif

+ 81 - 0
SDWebImage/UIView+WebCache.h

@@ -0,0 +1,81 @@
+/*
+ * This file is part of the SDWebImage package.
+ * (c) Olivier Poitrey <rs@dailymotion.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+#import "SDWebImageCompat.h"
+
+#if SD_UIKIT || SD_MAC
+
+#import "SDWebImageManager.h"
+
+typedef void(^SDSetImageBlock)(UIImage * _Nullable image, NSData * _Nullable imageData);
+
+@interface UIView (WebCache)
+
+/**
+ * Get the current image URL.
+ *
+ * Note that because of the limitations of categories this property can get out of sync
+ * if you use setImage: directly.
+ */
+- (nullable NSURL *)sd_imageURL;
+
+/**
+ * Set the imageView `image` with an `url` and optionally a placeholder image.
+ *
+ * The download is asynchronous and cached.
+ *
+ * @param url            The url for the image.
+ * @param placeholder    The image to be set initially, until the image request finishes.
+ * @param options        The options to use when downloading the image. @see SDWebImageOptions for the possible values.
+ * @param operationKey   A string to be used as the operation key. If nil, will use the class name
+ * @param setImageBlock  Block used for custom set image code
+ * @param progressBlock  A block called while image is downloading
+ * @param completedBlock A block called when operation has been completed. This block has no return value
+ *                       and takes the requested UIImage as first parameter. In case of error the image parameter
+ *                       is nil and the second parameter may contain an NSError. The third parameter is a Boolean
+ *                       indicating if the image was retrieved from the local cache or from the network.
+ *                       The fourth parameter is the original image url.
+ */
+- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
+                  placeholderImage:(nullable UIImage *)placeholder
+                           options:(SDWebImageOptions)options
+                      operationKey:(nullable NSString *)operationKey
+                     setImageBlock:(nullable SDSetImageBlock)setImageBlock
+                          progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
+                         completed:(nullable SDExternalCompletionBlock)completedBlock;
+
+/**
+ * Cancel the current download
+ */
+- (void)sd_cancelCurrentImageLoad;
+
+#if SD_UIKIT
+
+#pragma mark - Activity indicator
+
+/**
+ *  Show activity UIActivityIndicatorView
+ */
+- (void)setShowActivityIndicatorView:(BOOL)show;
+
+/**
+ *  set desired UIActivityIndicatorViewStyle
+ *
+ *  @param style The style of the UIActivityIndicatorView
+ */
+- (void)setIndicatorStyle:(UIActivityIndicatorViewStyle)style;
+
+- (BOOL)showActivityIndicatorView;
+- (void)addActivityIndicator;
+- (void)removeActivityIndicator;
+
+#endif
+
+@end
+
+#endif

+ 191 - 0
SDWebImage/UIView+WebCache.m

@@ -0,0 +1,191 @@
+/*
+ * This file is part of the SDWebImage package.
+ * (c) Olivier Poitrey <rs@dailymotion.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+#import "UIView+WebCache.h"
+
+#if SD_UIKIT || SD_MAC
+
+#import "objc/runtime.h"
+#import "UIView+WebCacheOperation.h"
+
+static char imageURLKey;
+
+#if SD_UIKIT
+static char TAG_ACTIVITY_INDICATOR;
+static char TAG_ACTIVITY_STYLE;
+#endif
+static char TAG_ACTIVITY_SHOW;
+
+@implementation UIView (WebCache)
+
+- (nullable NSURL *)sd_imageURL {
+    return objc_getAssociatedObject(self, &imageURLKey);
+}
+
+- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
+                  placeholderImage:(nullable UIImage *)placeholder
+                           options:(SDWebImageOptions)options
+                      operationKey:(nullable NSString *)operationKey
+                     setImageBlock:(nullable SDSetImageBlock)setImageBlock
+                          progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
+                         completed:(nullable SDExternalCompletionBlock)completedBlock {
+    NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
+    [self sd_cancelImageLoadOperationWithKey:validOperationKey];
+    objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+    
+    if (!(options & SDWebImageDelayPlaceholder)) {
+        dispatch_main_async_safe(^{
+            [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
+        });
+    }
+    
+    if (url) {
+        // check if activityView is enabled or not
+        if ([self showActivityIndicatorView]) {
+            [self addActivityIndicator];
+        }
+        
+        __weak __typeof(self)wself = self;
+        id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
+            __strong __typeof (wself) sself = wself;
+            [sself removeActivityIndicator];
+            if (!sself) {
+                return;
+            }
+            dispatch_main_sync_safe(^{
+                if (!sself) {
+                    return;
+                }
+                if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) {
+                    completedBlock(image, error, cacheType, url);
+                    return;
+                } else if (image) {
+                    [sself sd_setImage:image imageData:data basedOnClassOrViaCustomSetImageBlock:setImageBlock];
+                    [sself sd_setNeedsLayout];
+                } else {
+                    if ((options & SDWebImageDelayPlaceholder)) {
+                        [sself sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
+                        [sself sd_setNeedsLayout];
+                    }
+                }
+                if (completedBlock && finished) {
+                    completedBlock(image, error, cacheType, url);
+                }
+            });
+        }];
+        [self sd_setImageLoadOperation:operation forKey:validOperationKey];
+    } else {
+        dispatch_main_async_safe(^{
+            [self removeActivityIndicator];
+            if (completedBlock) {
+                NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
+                completedBlock(nil, error, SDImageCacheTypeNone, url);
+            }
+        });
+    }
+}
+
+- (void)sd_cancelCurrentImageLoad {
+    [self sd_cancelImageLoadOperationWithKey:NSStringFromClass([self class])];
+}
+
+- (void)sd_setImage:(UIImage *)image imageData:(NSData *)imageData basedOnClassOrViaCustomSetImageBlock:(SDSetImageBlock)setImageBlock {
+    if (setImageBlock) {
+        setImageBlock(image, imageData);
+    } else if ([self isKindOfClass:[UIImageView class]]) {
+        UIImageView *imageView = (UIImageView *)self;
+        imageView.image = image;
+    } else if ([self isKindOfClass:[UIButton class]]) {
+        UIButton *button = (UIButton *)self;
+        [button setImage:image forState:UIControlStateNormal];
+    }
+}
+
+- (void)sd_setNeedsLayout {
+#if SD_UIKIT
+    [self setNeedsLayout];
+#elif SD_MAC
+    [self setNeedsLayout:YES];
+#endif
+}
+
+#pragma mark - Activity indicator
+
+#pragma mark -
+#if SD_UIKIT
+- (UIActivityIndicatorView *)activityIndicator {
+    return (UIActivityIndicatorView *)objc_getAssociatedObject(self, &TAG_ACTIVITY_INDICATOR);
+}
+
+- (void)setActivityIndicator:(UIActivityIndicatorView *)activityIndicator {
+    objc_setAssociatedObject(self, &TAG_ACTIVITY_INDICATOR, activityIndicator, OBJC_ASSOCIATION_RETAIN);
+}
+#endif
+
+- (void)setShowActivityIndicatorView:(BOOL)show {
+    objc_setAssociatedObject(self, &TAG_ACTIVITY_SHOW, @(show), OBJC_ASSOCIATION_RETAIN);
+}
+
+- (BOOL)showActivityIndicatorView {
+    return [objc_getAssociatedObject(self, &TAG_ACTIVITY_SHOW) boolValue];
+}
+
+#if SD_UIKIT
+- (void)setIndicatorStyle:(UIActivityIndicatorViewStyle)style{
+    objc_setAssociatedObject(self, &TAG_ACTIVITY_STYLE, [NSNumber numberWithInt:style], OBJC_ASSOCIATION_RETAIN);
+}
+
+- (int)getIndicatorStyle{
+    return [objc_getAssociatedObject(self, &TAG_ACTIVITY_STYLE) intValue];
+}
+#endif
+
+- (void)addActivityIndicator {
+#if SD_UIKIT
+    if (!self.activityIndicator) {
+        self.activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:[self getIndicatorStyle]];
+        self.activityIndicator.translatesAutoresizingMaskIntoConstraints = NO;
+        
+        dispatch_main_async_safe(^{
+            [self addSubview:self.activityIndicator];
+            
+            [self addConstraint:[NSLayoutConstraint constraintWithItem:self.activityIndicator
+                                                             attribute:NSLayoutAttributeCenterX
+                                                             relatedBy:NSLayoutRelationEqual
+                                                                toItem:self
+                                                             attribute:NSLayoutAttributeCenterX
+                                                            multiplier:1.0
+                                                              constant:0.0]];
+            [self addConstraint:[NSLayoutConstraint constraintWithItem:self.activityIndicator
+                                                             attribute:NSLayoutAttributeCenterY
+                                                             relatedBy:NSLayoutRelationEqual
+                                                                toItem:self
+                                                             attribute:NSLayoutAttributeCenterY
+                                                            multiplier:1.0
+                                                              constant:0.0]];
+        });
+    }
+    
+    dispatch_main_async_safe(^{
+        [self.activityIndicator startAnimating];
+    });
+#endif
+}
+
+- (void)removeActivityIndicator {
+#if SD_UIKIT
+    if (self.activityIndicator) {
+        [self.activityIndicator removeFromSuperview];
+        self.activityIndicator = nil;
+    }
+#endif
+}
+
+@end
+
+#endif