UIButton+WebCache.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /*
  2. * This file is part of the SDWebImage package.
  3. * (c) Olivier Poitrey <rs@dailymotion.com>
  4. *
  5. * For the full copyright and license information, please view the LICENSE
  6. * file that was distributed with this source code.
  7. */
  8. #import "UIButton+WebCache.h"
  9. #if SD_UIKIT
  10. #import "objc/runtime.h"
  11. #import "UIView+WebCacheOperation.h"
  12. #import "UIView+WebCache.h"
  13. #import "SDInternalMacros.h"
  14. static char imageURLStorageKey;
  15. typedef NSMutableDictionary<NSString *, NSURL *> SDStateImageURLDictionary;
  16. static inline NSString * imageURLKeyForState(UIControlState state) {
  17. return [NSString stringWithFormat:@"image_%lu", (unsigned long)state];
  18. }
  19. static inline NSString * backgroundImageURLKeyForState(UIControlState state) {
  20. return [NSString stringWithFormat:@"backgroundImage_%lu", (unsigned long)state];
  21. }
  22. static inline NSString * imageOperationKeyForState(UIControlState state) {
  23. return [NSString stringWithFormat:@"UIButtonImageOperation%lu", (unsigned long)state];
  24. }
  25. static inline NSString * backgroundImageOperationKeyForState(UIControlState state) {
  26. return [NSString stringWithFormat:@"UIButtonBackgroundImageOperation%lu", (unsigned long)state];
  27. }
  28. @implementation UIButton (WebCache)
  29. #pragma mark - Image
  30. - (nullable NSURL *)sd_currentImageURL {
  31. NSURL *url = self.sd_imageURLStorage[imageURLKeyForState(self.state)];
  32. if (!url) {
  33. url = self.sd_imageURLStorage[imageURLKeyForState(UIControlStateNormal)];
  34. }
  35. return url;
  36. }
  37. - (nullable NSURL *)sd_imageURLForState:(UIControlState)state {
  38. return self.sd_imageURLStorage[imageURLKeyForState(state)];
  39. }
  40. - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state {
  41. [self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil];
  42. }
  43. - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder {
  44. [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil];
  45. }
  46. - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options {
  47. [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:options progress:nil completed:nil];
  48. }
  49. - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options context:(nullable SDWebImageContext *)context {
  50. [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:options context:context progress:nil completed:nil];
  51. }
  52. - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state completed:(nullable SDExternalCompletionBlock)completedBlock {
  53. [self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:completedBlock];
  54. }
  55. - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder completed:(nullable SDExternalCompletionBlock)completedBlock {
  56. [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:completedBlock];
  57. }
  58. - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options completed:(nullable SDExternalCompletionBlock)completedBlock {
  59. [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:options progress:nil completed:completedBlock];
  60. }
  61. - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options progress:(nullable SDImageLoaderProgressBlock)progressBlock completed:(nullable SDExternalCompletionBlock)completedBlock {
  62. [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:options context:nil progress:progressBlock completed:completedBlock];
  63. }
  64. - (void)sd_setImageWithURL:(nullable NSURL *)url
  65. forState:(UIControlState)state
  66. placeholderImage:(nullable UIImage *)placeholder
  67. options:(SDWebImageOptions)options
  68. context:(nullable SDWebImageContext *)context
  69. progress:(nullable SDImageLoaderProgressBlock)progressBlock
  70. completed:(nullable SDExternalCompletionBlock)completedBlock {
  71. if (!url) {
  72. [self.sd_imageURLStorage removeObjectForKey:imageURLKeyForState(state)];
  73. } else {
  74. self.sd_imageURLStorage[imageURLKeyForState(state)] = url;
  75. }
  76. SDWebImageMutableContext *mutableContext;
  77. if (context) {
  78. mutableContext = [context mutableCopy];
  79. } else {
  80. mutableContext = [NSMutableDictionary dictionary];
  81. }
  82. mutableContext[SDWebImageContextSetImageOperationKey] = imageOperationKeyForState(state);
  83. @weakify(self);
  84. [self sd_internalSetImageWithURL:url
  85. placeholderImage:placeholder
  86. options:options
  87. context:mutableContext
  88. setImageBlock:^(UIImage * _Nullable image, NSData * _Nullable imageData, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
  89. @strongify(self);
  90. [self setImage:image forState:state];
  91. }
  92. progress:progressBlock
  93. completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
  94. if (completedBlock) {
  95. completedBlock(image, error, cacheType, imageURL);
  96. }
  97. }];
  98. }
  99. #pragma mark - Background Image
  100. - (nullable NSURL *)sd_currentBackgroundImageURL {
  101. NSURL *url = self.sd_imageURLStorage[backgroundImageURLKeyForState(self.state)];
  102. if (!url) {
  103. url = self.sd_imageURLStorage[backgroundImageURLKeyForState(UIControlStateNormal)];
  104. }
  105. return url;
  106. }
  107. - (nullable NSURL *)sd_backgroundImageURLForState:(UIControlState)state {
  108. return self.sd_imageURLStorage[backgroundImageURLKeyForState(state)];
  109. }
  110. - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state {
  111. [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil];
  112. }
  113. - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder {
  114. [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil];
  115. }
  116. - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options {
  117. [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:options progress:nil completed:nil];
  118. }
  119. - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options context:(nullable SDWebImageContext *)context {
  120. [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:options context:context progress:nil completed:nil];
  121. }
  122. - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state completed:(nullable SDExternalCompletionBlock)completedBlock {
  123. [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:completedBlock];
  124. }
  125. - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder completed:(nullable SDExternalCompletionBlock)completedBlock {
  126. [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:completedBlock];
  127. }
  128. - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options completed:(nullable SDExternalCompletionBlock)completedBlock {
  129. [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 progress:nil completed:completedBlock];
  130. }
  131. - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options progress:(nullable SDImageLoaderProgressBlock)progressBlock completed:(nullable SDExternalCompletionBlock)completedBlock {
  132. [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 context:nil progress:progressBlock completed:completedBlock];
  133. }
  134. - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url
  135. forState:(UIControlState)state
  136. placeholderImage:(nullable UIImage *)placeholder
  137. options:(SDWebImageOptions)options
  138. context:(nullable SDWebImageContext *)context
  139. progress:(nullable SDImageLoaderProgressBlock)progressBlock
  140. completed:(nullable SDExternalCompletionBlock)completedBlock {
  141. if (!url) {
  142. [self.sd_imageURLStorage removeObjectForKey:backgroundImageURLKeyForState(state)];
  143. } else {
  144. self.sd_imageURLStorage[backgroundImageURLKeyForState(state)] = url;
  145. }
  146. SDWebImageMutableContext *mutableContext;
  147. if (context) {
  148. mutableContext = [context mutableCopy];
  149. } else {
  150. mutableContext = [NSMutableDictionary dictionary];
  151. }
  152. mutableContext[SDWebImageContextSetImageOperationKey] = imageOperationKeyForState(state);
  153. @weakify(self);
  154. [self sd_internalSetImageWithURL:url
  155. placeholderImage:placeholder
  156. options:options
  157. context:mutableContext
  158. setImageBlock:^(UIImage * _Nullable image, NSData * _Nullable imageData, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
  159. @strongify(self);
  160. [self setBackgroundImage:image forState:state];
  161. }
  162. progress:progressBlock
  163. completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
  164. if (completedBlock) {
  165. completedBlock(image, error, cacheType, imageURL);
  166. }
  167. }];
  168. }
  169. #pragma mark - Cancel
  170. - (void)sd_cancelImageLoadForState:(UIControlState)state {
  171. [self sd_cancelImageLoadOperationWithKey:imageOperationKeyForState(state)];
  172. }
  173. - (void)sd_cancelBackgroundImageLoadForState:(UIControlState)state {
  174. [self sd_cancelImageLoadOperationWithKey:backgroundImageOperationKeyForState(state)];
  175. }
  176. #pragma mark - Private
  177. - (SDStateImageURLDictionary *)sd_imageURLStorage {
  178. SDStateImageURLDictionary *storage = objc_getAssociatedObject(self, &imageURLStorageKey);
  179. if (!storage) {
  180. storage = [NSMutableDictionary dictionary];
  181. objc_setAssociatedObject(self, &imageURLStorageKey, storage, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  182. }
  183. return storage;
  184. }
  185. @end
  186. #endif