123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- //
- // UIButton+YYWebImage.m
- // YYKit <https://github.com/ibireme/YYKit>
- //
- // Created by ibireme on 15/2/23.
- // Copyright (c) 2015 ibireme.
- //
- // This source code is licensed under the MIT-style license found in the
- // LICENSE file in the root directory of this source tree.
- //
- #import "UIButton+YYWebImage.h"
- #import "YYWebImageOperation.h"
- #import "_YYWebImageSetter.h"
- #import <libkern/OSAtomic.h>
- #import <objc/runtime.h>
- // Dummy class for category
- @interface UIButton_YYWebImage : NSObject @end
- @implementation UIButton_YYWebImage @end
- static inline NSNumber *UIControlStateSingle(UIControlState state) {
- if (state & UIControlStateHighlighted) return @(UIControlStateHighlighted);
- if (state & UIControlStateDisabled) return @(UIControlStateDisabled);
- if (state & UIControlStateSelected) return @(UIControlStateSelected);
- return @(UIControlStateNormal);
- }
- static inline NSArray *UIControlStateMulti(UIControlState state) {
- NSMutableArray *array = [NSMutableArray new];
- if (state & UIControlStateHighlighted) [array addObject:@(UIControlStateHighlighted)];
- if (state & UIControlStateDisabled) [array addObject:@(UIControlStateDisabled)];
- if (state & UIControlStateSelected) [array addObject:@(UIControlStateSelected)];
- if ((state & 0xFF) == 0) [array addObject:@(UIControlStateNormal)];
- return array;
- }
- static int _YYWebImageSetterKey;
- static int _YYWebImageBackgroundSetterKey;
- @interface _YYWebImageSetterDicForButton : NSObject
- - (_YYWebImageSetter *)setterForState:(NSNumber *)state;
- - (_YYWebImageSetter *)lazySetterForState:(NSNumber *)state;
- @end
- @implementation _YYWebImageSetterDicForButton {
- NSMutableDictionary *_dic;
- OSSpinLock _lock;
- }
- - (instancetype)init {
- self = [super init];
- _lock = OS_SPINLOCK_INIT;
- _dic = [NSMutableDictionary new];
- return self;
- }
- - (_YYWebImageSetter *)setterForState:(NSNumber *)state {
- OSSpinLockLock(&_lock);
- _YYWebImageSetter *setter = _dic[state];
- OSSpinLockUnlock(&_lock);
- return setter;
-
- }
- - (_YYWebImageSetter *)lazySetterForState:(NSNumber *)state {
- OSSpinLockLock(&_lock);
- _YYWebImageSetter *setter = _dic[state];
- if (!setter) {
- setter = [_YYWebImageSetter new];
- _dic[state] = setter;
- }
- OSSpinLockUnlock(&_lock);
- return setter;
- }
- @end
- @implementation UIButton (YYWebImage)
- #pragma mark - image
- - (void)_yy_setImageWithURL:(NSURL *)imageURL
- forSingleState:(NSNumber *)state
- placeholder:(UIImage *)placeholder
- options:(YYWebImageOptions)options
- manager:(YYWebImageManager *)manager
- progress:(YYWebImageProgressBlock)progress
- transform:(YYWebImageTransformBlock)transform
- completion:(YYWebImageCompletionBlock)completion {
- if ([imageURL isKindOfClass:[NSString class]]) imageURL = [NSURL URLWithString:(id)imageURL];
- manager = manager ? manager : [YYWebImageManager sharedManager];
-
- _YYWebImageSetterDicForButton *dic = objc_getAssociatedObject(self, &_YYWebImageSetterKey);
- if (!dic) {
- dic = [_YYWebImageSetterDicForButton new];
- objc_setAssociatedObject(self, &_YYWebImageSetterKey, dic, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- _YYWebImageSetter *setter = [dic lazySetterForState:state];
- int32_t sentinel = [setter cancelWithNewURL:imageURL];
-
- _yy_dispatch_sync_on_main_queue(^{
- if (!imageURL) {
- if (!(options & YYWebImageOptionIgnorePlaceHolder)) {
- [self setImage:placeholder forState:state.integerValue];
- }
- return;
- }
-
- // get the image from memory as quickly as possible
- UIImage *imageFromMemory = nil;
- if (manager.cache &&
- !(options & YYWebImageOptionUseNSURLCache) &&
- !(options & YYWebImageOptionRefreshImageCache)) {
- imageFromMemory = [manager.cache getImageForKey:[manager cacheKeyForURL:imageURL] withType:YYImageCacheTypeMemory];
- }
- if (imageFromMemory) {
- if (!(options & YYWebImageOptionAvoidSetImage)) {
- [self setImage:imageFromMemory forState:state.integerValue];
- }
- if(completion) completion(imageFromMemory, imageURL, YYWebImageFromMemoryCacheFast, YYWebImageStageFinished, nil);
- return;
- }
-
-
- if (!(options & YYWebImageOptionIgnorePlaceHolder)) {
- [self setImage:placeholder forState:state.integerValue];
- }
-
- __weak typeof(self) _self = self;
- dispatch_async([_YYWebImageSetter setterQueue], ^{
- YYWebImageProgressBlock _progress = nil;
- if (progress) _progress = ^(NSInteger receivedSize, NSInteger expectedSize) {
- dispatch_async(dispatch_get_main_queue(), ^{
- progress(receivedSize, expectedSize);
- });
- };
-
- YYWebImageCompletionBlock _completion = ^(UIImage *image, NSURL *url, YYWebImageFromType from, YYWebImageStage stage, NSError *error) {
- __strong typeof(_self) self = _self;
- BOOL setImage = (stage == YYWebImageStageFinished || stage == YYWebImageStageProgress) && image && !(options & YYWebImageOptionAvoidSetImage);
- dispatch_async(dispatch_get_main_queue(), ^{
- if (setImage && self) {
- [self setImage:image forState:state.integerValue];
- }
- if (completion) completion(image, url, from, stage, error);
- });
- };
-
- [setter setOperationWithSentinel:sentinel url:imageURL options:options manager:manager progress:_progress transform:transform completion:_completion];
- });
- });
- }
- - (void)_yy_cancelImageRequestForSingleState:(NSNumber *)state {
- _YYWebImageSetterDicForButton *dic = objc_getAssociatedObject(self, &_YYWebImageSetterKey);
- _YYWebImageSetter *setter = [dic setterForState:state];
- if (setter) [setter cancel];
- }
- - (NSURL *)yy_imageURLForState:(UIControlState)state {
- _YYWebImageSetterDicForButton *dic = objc_getAssociatedObject(self, &_YYWebImageSetterKey);
- _YYWebImageSetter *setter = [dic setterForState:UIControlStateSingle(state)];
- return setter.imageURL;
- }
- - (void)yy_setImageWithURL:(NSURL *)imageURL
- forState:(UIControlState)state
- placeholder:(UIImage *)placeholder {
- [self yy_setImageWithURL:imageURL
- forState:state
- placeholder:placeholder
- options:kNilOptions
- manager:nil
- progress:nil
- transform:nil
- completion:nil];
- }
- - (void)yy_setImageWithURL:(NSURL *)imageURL
- forState:(UIControlState)state
- options:(YYWebImageOptions)options {
- [self yy_setImageWithURL:imageURL
- forState:state
- placeholder:nil
- options:options
- manager:nil
- progress:nil
- transform:nil
- completion:nil];
- }
- - (void)yy_setImageWithURL:(NSURL *)imageURL
- forState:(UIControlState)state
- placeholder:(UIImage *)placeholder
- options:(YYWebImageOptions)options
- completion:(YYWebImageCompletionBlock)completion {
- [self yy_setImageWithURL:imageURL
- forState:state
- placeholder:placeholder
- options:options
- manager:nil
- progress:nil
- transform:nil
- completion:completion];
- }
- - (void)yy_setImageWithURL:(NSURL *)imageURL
- forState:(UIControlState)state
- placeholder:(UIImage *)placeholder
- options:(YYWebImageOptions)options
- progress:(YYWebImageProgressBlock)progress
- transform:(YYWebImageTransformBlock)transform
- completion:(YYWebImageCompletionBlock)completion {
- [self yy_setImageWithURL:imageURL
- forState:state
- placeholder:placeholder
- options:options
- manager:nil
- progress:progress
- transform:transform
- completion:completion];
- }
- - (void)yy_setImageWithURL:(NSURL *)imageURL
- forState:(UIControlState)state
- placeholder:(UIImage *)placeholder
- options:(YYWebImageOptions)options
- manager:(YYWebImageManager *)manager
- progress:(YYWebImageProgressBlock)progress
- transform:(YYWebImageTransformBlock)transform
- completion:(YYWebImageCompletionBlock)completion {
- for (NSNumber *num in UIControlStateMulti(state)) {
- [self _yy_setImageWithURL:imageURL
- forSingleState:num
- placeholder:placeholder
- options:options
- manager:manager
- progress:progress
- transform:transform
- completion:completion];
- }
- }
- - (void)yy_cancelImageRequestForState:(UIControlState)state {
- for (NSNumber *num in UIControlStateMulti(state)) {
- [self _yy_cancelImageRequestForSingleState:num];
- }
- }
- #pragma mark - background image
- - (void)_yy_setBackgroundImageWithURL:(NSURL *)imageURL
- forSingleState:(NSNumber *)state
- placeholder:(UIImage *)placeholder
- options:(YYWebImageOptions)options
- manager:(YYWebImageManager *)manager
- progress:(YYWebImageProgressBlock)progress
- transform:(YYWebImageTransformBlock)transform
- completion:(YYWebImageCompletionBlock)completion {
- if ([imageURL isKindOfClass:[NSString class]]) imageURL = [NSURL URLWithString:(id)imageURL];
- manager = manager ? manager : [YYWebImageManager sharedManager];
-
- _YYWebImageSetterDicForButton *dic = objc_getAssociatedObject(self, &_YYWebImageBackgroundSetterKey);
- if (!dic) {
- dic = [_YYWebImageSetterDicForButton new];
- objc_setAssociatedObject(self, &_YYWebImageBackgroundSetterKey, dic, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- _YYWebImageSetter *setter = [dic lazySetterForState:state];
- int32_t sentinel = [setter cancelWithNewURL:imageURL];
-
- _yy_dispatch_sync_on_main_queue(^{
- if (!imageURL) {
- if (!(options & YYWebImageOptionIgnorePlaceHolder)) {
- [self setBackgroundImage:placeholder forState:state.integerValue];
- }
- return;
- }
-
- // get the image from memory as quickly as possible
- UIImage *imageFromMemory = nil;
- if (manager.cache &&
- !(options & YYWebImageOptionUseNSURLCache) &&
- !(options & YYWebImageOptionRefreshImageCache)) {
- imageFromMemory = [manager.cache getImageForKey:[manager cacheKeyForURL:imageURL] withType:YYImageCacheTypeMemory];
- }
- if (imageFromMemory) {
- if (!(options & YYWebImageOptionAvoidSetImage)) {
- [self setBackgroundImage:imageFromMemory forState:state.integerValue];
- }
- if(completion) completion(imageFromMemory, imageURL, YYWebImageFromMemoryCacheFast, YYWebImageStageFinished, nil);
- return;
- }
-
-
- if (!(options & YYWebImageOptionIgnorePlaceHolder)) {
- [self setBackgroundImage:placeholder forState:state.integerValue];
- }
-
- __weak typeof(self) _self = self;
- dispatch_async([_YYWebImageSetter setterQueue], ^{
- YYWebImageProgressBlock _progress = nil;
- if (progress) _progress = ^(NSInteger receivedSize, NSInteger expectedSize) {
- dispatch_async(dispatch_get_main_queue(), ^{
- progress(receivedSize, expectedSize);
- });
- };
-
- YYWebImageCompletionBlock _completion = ^(UIImage *image, NSURL *url, YYWebImageFromType from, YYWebImageStage stage, NSError *error) {
- __strong typeof(_self) self = _self;
- BOOL setImage = (stage == YYWebImageStageFinished || stage == YYWebImageStageProgress) && image && !(options & YYWebImageOptionAvoidSetImage);
- dispatch_async(dispatch_get_main_queue(), ^{
- if (setImage && self) {
- [self setBackgroundImage:image forState:state.integerValue];
- }
- if (completion) completion(image, url, from, stage, error);
- });
- };
-
- [setter setOperationWithSentinel:sentinel url:imageURL options:options manager:manager progress:_progress transform:transform completion:_completion];
- });
- });
- }
- - (void)_yy_cancelBackgroundImageRequestForSingleState:(NSNumber *)state {
- _YYWebImageSetterDicForButton *dic = objc_getAssociatedObject(self, &_YYWebImageBackgroundSetterKey);
- _YYWebImageSetter *setter = [dic setterForState:state];
- if (setter) [setter cancel];
- }
- - (NSURL *)yy_backgroundImageURLForState:(UIControlState)state {
- _YYWebImageSetterDicForButton *dic = objc_getAssociatedObject(self, &_YYWebImageBackgroundSetterKey);
- _YYWebImageSetter *setter = [dic setterForState:UIControlStateSingle(state)];
- return setter.imageURL;
- }
- - (void)yy_setBackgroundImageWithURL:(NSURL *)imageURL
- forState:(UIControlState)state
- placeholder:(UIImage *)placeholder {
- [self yy_setBackgroundImageWithURL:imageURL
- forState:state
- placeholder:placeholder
- options:kNilOptions
- manager:nil
- progress:nil
- transform:nil
- completion:nil];
- }
- - (void)yy_setBackgroundImageWithURL:(NSURL *)imageURL
- forState:(UIControlState)state
- options:(YYWebImageOptions)options {
- [self yy_setBackgroundImageWithURL:imageURL
- forState:state
- placeholder:nil
- options:options
- manager:nil
- progress:nil
- transform:nil
- completion:nil];
- }
- - (void)yy_setBackgroundImageWithURL:(NSURL *)imageURL
- forState:(UIControlState)state
- placeholder:(UIImage *)placeholder
- options:(YYWebImageOptions)options
- completion:(YYWebImageCompletionBlock)completion {
- [self yy_setBackgroundImageWithURL:imageURL
- forState:state
- placeholder:placeholder
- options:options
- manager:nil
- progress:nil
- transform:nil
- completion:completion];
- }
- - (void)yy_setBackgroundImageWithURL:(NSURL *)imageURL
- forState:(UIControlState)state
- placeholder:(UIImage *)placeholder
- options:(YYWebImageOptions)options
- progress:(YYWebImageProgressBlock)progress
- transform:(YYWebImageTransformBlock)transform
- completion:(YYWebImageCompletionBlock)completion {
- [self yy_setBackgroundImageWithURL:imageURL
- forState:state
- placeholder:placeholder
- options:options
- manager:nil
- progress:progress
- transform:transform
- completion:completion];
- }
- - (void)yy_setBackgroundImageWithURL:(NSURL *)imageURL
- forState:(UIControlState)state
- placeholder:(UIImage *)placeholder
- options:(YYWebImageOptions)options
- manager:(YYWebImageManager *)manager
- progress:(YYWebImageProgressBlock)progress
- transform:(YYWebImageTransformBlock)transform
- completion:(YYWebImageCompletionBlock)completion {
- for (NSNumber *num in UIControlStateMulti(state)) {
- [self _yy_setBackgroundImageWithURL:imageURL
- forSingleState:num
- placeholder:placeholder
- options:options
- manager:manager
- progress:progress
- transform:transform
- completion:completion];
- }
- }
- - (void)yy_cancelBackgroundImageRequestForState:(UIControlState)state {
- for (NSNumber *num in UIControlStateMulti(state)) {
- [self _yy_cancelBackgroundImageRequestForSingleState:num];
- }
- }
- @end
|