0% found this document useful (0 votes)
51 views7 pages

UIView WebCache

This document is a header file that defines methods for adding image loading and caching capabilities to UIView. It defines methods for setting an image URL on a view, tracking image load progress, canceling current loads, and setting images on views via blocks or default behavior. It uses associated objects to store image URLs and loads on views and manages loading and caching images using SDWebImageManager.

Uploaded by

istv4n
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
51 views7 pages

UIView WebCache

This document is a header file that defines methods for adding image loading and caching capabilities to UIView. It defines methods for setting an image URL on a view, tracking image load progress, canceling current loads, and setting images on views via blocks or default behavior. It uses associated objects to store image URLs and loads on views and manages loading and caching images using SDWebImageManager.

Uploaded by

istv4n
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd

/*

* This file is part of the SDWebImage package.


* (c) Olivier Poitrey <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

#import "UIView+WebCache.h"
#import "objc/runtime.h"
#import "UIView+WebCacheOperation.h"

NSString * const SDWebImageInternalSetImageGroupKey = @"internalSetImageGroup";


NSString * const SDWebImageExternalCustomManagerKey = @"externalCustomManager";

const int64_t SDWebImageProgressUnitCountUnknown = 1LL;

static char imageURLKey;

#if SD_UIKIT
static char TAG_ACTIVITY_INDICATOR;
static char TAG_ACTIVITY_STYLE;
static char TAG_ACTIVITY_SHOW;
#endif

@implementation UIView (WebCache)

- (nullable NSURL *)sd_imageURL {


return objc_getAssociatedObject(self, &imageURLKey);
}

- (NSProgress *)sd_imageProgress {
NSProgress *progress = objc_getAssociatedObject(self,
@selector(sd_imageProgress));
if (!progress) {
progress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
self.sd_imageProgress = progress;
}
return progress;
}

- (void)setSd_imageProgress:(NSProgress *)sd_imageProgress {
objc_setAssociatedObject(self, @selector(sd_imageProgress), sd_imageProgress,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (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 {
return [self sd_internalSetImageWithURL:url placeholderImage:placeholder
options:options operationKey:operationKey setImageBlock:setImageBlock
progress:progressBlock completed:completedBlock context:nil];
}
- (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
context:(nullable NSDictionary<NSString *, id> *)context
{
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) {
#if SD_UIKIT
// check if activityView is enabled or not
if ([self sd_showActivityIndicatorView]) {
[self sd_addActivityIndicator];
}
#endif

// reset the progress


self.sd_imageProgress.totalUnitCount = 0;
self.sd_imageProgress.completedUnitCount = 0;

SDWebImageManager *manager;
if ([context valueForKey:SDWebImageExternalCustomManagerKey]) {
manager = (SDWebImageManager *)[context
valueForKey:SDWebImageExternalCustomManagerKey];
} else {
manager = [SDWebImageManager sharedManager];
}

__weak __typeof(self)wself = self;


SDWebImageDownloaderProgressBlock combinedProgressBlock = ^(NSInteger
receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
wself.sd_imageProgress.totalUnitCount = expectedSize;
wself.sd_imageProgress.completedUnitCount = receivedSize;
if (progressBlock) {
progressBlock(receivedSize, expectedSize, targetURL);
}
};
id <SDWebImageOperation> operation = [manager loadImageWithURL:url
options:options progress:combinedProgressBlock completed:^(UIImage *image, NSData
*data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL)
{
__strong __typeof (wself) sself = wself;
if (!sself) { return; }
#if SD_UIKIT
[sself sd_removeActivityIndicator];
#endif
// if the progress not been updated, mark it to complete state
if (finished && !error && sself.sd_imageProgress.totalUnitCount == 0 &&
sself.sd_imageProgress.completedUnitCount == 0) {
sself.sd_imageProgress.totalUnitCount =
SDWebImageProgressUnitCountUnknown;
sself.sd_imageProgress.completedUnitCount =
SDWebImageProgressUnitCountUnknown;
}
BOOL shouldCallCompletedBlock = finished || (options &
SDWebImageAvoidAutoSetImage);
BOOL shouldNotSetImage = ((image && (options &
SDWebImageAvoidAutoSetImage)) ||
(!image && !(options &
SDWebImageDelayPlaceholder)));
SDWebImageNoParamsBlock callCompletedBlockClojure = ^{
if (!sself) { return; }
if (!shouldNotSetImage) {
[sself sd_setNeedsLayout];
}
if (completedBlock && shouldCallCompletedBlock) {
completedBlock(image, error, cacheType, url);
}
};

// case 1a: we got an image, but the SDWebImageAvoidAutoSetImage flag


is set
// OR
// case 1b: we got no image and the SDWebImageDelayPlaceholder is not
set
if (shouldNotSetImage) {
dispatch_main_async_safe(callCompletedBlockClojure);
return;
}

UIImage *targetImage = nil;


NSData *targetData = nil;
if (image) {
// case 2a: we got an image and the SDWebImageAvoidAutoSetImage is
not set
targetImage = image;
targetData = data;
} else if (options & SDWebImageDelayPlaceholder) {
// case 2b: we got no image and the SDWebImageDelayPlaceholder flag
is set
targetImage = placeholder;
targetData = nil;
}

#if SD_UIKIT || SD_MAC


// check whether we should use the image transition
SDWebImageTransition *transition = nil;
if (finished && (options & SDWebImageForceTransition || cacheType ==
SDImageCacheTypeNone)) {
transition = sself.sd_imageTransition;
}
#endif
dispatch_main_async_safe(^{
#if SD_UIKIT || SD_MAC
[sself sd_setImage:targetImage imageData:targetData
basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:transition
cacheType:cacheType imageURL:imageURL];
#else
[sself sd_setImage:targetImage imageData:targetData
basedOnClassOrViaCustomSetImageBlock:setImageBlock];
#endif
callCompletedBlockClojure();
});
}];
[self sd_setImageLoadOperation:operation forKey:validOperationKey];
} else {
dispatch_main_async_safe(^{
#if SD_UIKIT
[self sd_removeActivityIndicator];
#endif
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 SD_UIKIT || SD_MAC
[self sd_setImage:image imageData:imageData
basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:nil cacheType:0
imageURL:nil];
#else
// watchOS does not support view transition. Simplify the logic
if (setImageBlock) {
setImageBlock(image, imageData);
} else if ([self isKindOfClass:[UIImageView class]]) {
UIImageView *imageView = (UIImageView *)self;
[imageView setImage:image];
}
#endif
}

#if SD_UIKIT || SD_MAC


- (void)sd_setImage:(UIImage *)image imageData:(NSData *)imageData
basedOnClassOrViaCustomSetImageBlock:(SDSetImageBlock)setImageBlock transition:
(SDWebImageTransition *)transition cacheType:(SDImageCacheType)cacheType imageURL:
(NSURL *)imageURL {
UIView *view = self;
SDSetImageBlock finalSetImageBlock;
if (setImageBlock) {
finalSetImageBlock = setImageBlock;
} else if ([view isKindOfClass:[UIImageView class]]) {
UIImageView *imageView = (UIImageView *)view;
finalSetImageBlock = ^(UIImage *setImage, NSData *setImageData) {
imageView.image = setImage;
};
}
#if SD_UIKIT
else if ([view isKindOfClass:[UIButton class]]) {
UIButton *button = (UIButton *)view;
finalSetImageBlock = ^(UIImage *setImage, NSData *setImageData){
[button setImage:setImage forState:UIControlStateNormal];
};
}
#endif

if (transition) {
#if SD_UIKIT
[UIView transitionWithView:view duration:0 options:0 animations:^{
// 0 duration to let UIKit render placeholder and prepares block
if (transition.prepares) {
transition.prepares(view, image, imageData, cacheType, imageURL);
}
} completion:^(BOOL finished) {
[UIView transitionWithView:view duration:transition.duration
options:transition.animationOptions animations:^{
if (finalSetImageBlock && !transition.avoidAutoSetImage) {
finalSetImageBlock(image, imageData);
}
if (transition.animations) {
transition.animations(view, image);
}
} completion:transition.completion];
}];
#elif SD_MAC
[NSAnimationContext runAnimationGroup:^(NSAnimationContext * _Nonnull
prepareContext) {
// 0 duration to let AppKit render placeholder and prepares block
prepareContext.duration = 0;
if (transition.prepares) {
transition.prepares(view, image, imageData, cacheType, imageURL);
}
} completionHandler:^{
[NSAnimationContext runAnimationGroup:^(NSAnimationContext * _Nonnull
context) {
context.duration = transition.duration;
context.timingFunction = transition.timingFunction;
context.allowsImplicitAnimation = (transition.animationOptions &
SDWebImageAnimationOptionAllowsImplicitAnimation);
if (finalSetImageBlock && !transition.avoidAutoSetImage) {
finalSetImageBlock(image, imageData);
}
if (transition.animations) {
transition.animations(view, image);
}
} completionHandler:^{
if (transition.completion) {
transition.completion(YES);
}
}];
}];
#endif
} else {
if (finalSetImageBlock) {
finalSetImageBlock(image, imageData);
}
}
}
#endif

- (void)sd_setNeedsLayout {
#if SD_UIKIT
[self setNeedsLayout];
#elif SD_MAC
[self setNeedsLayout:YES];
#elif SD_WATCH
// Do nothing because WatchKit automatically layout the view after property
change
#endif
}

#if SD_UIKIT || SD_MAC

#pragma mark - Image Transition


- (SDWebImageTransition *)sd_imageTransition {
return objc_getAssociatedObject(self, @selector(sd_imageTransition));
}

- (void)setSd_imageTransition:(SDWebImageTransition *)sd_imageTransition {
objc_setAssociatedObject(self, @selector(sd_imageTransition),
sd_imageTransition, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

#if SD_UIKIT

#pragma mark - Activity indicator


- (UIActivityIndicatorView *)activityIndicator {
return (UIActivityIndicatorView *)objc_getAssociatedObject(self,
&TAG_ACTIVITY_INDICATOR);
}

- (void)setActivityIndicator:(UIActivityIndicatorView *)activityIndicator {
objc_setAssociatedObject(self, &TAG_ACTIVITY_INDICATOR, activityIndicator,
OBJC_ASSOCIATION_RETAIN);
}

- (void)sd_setShowActivityIndicatorView:(BOOL)show {
objc_setAssociatedObject(self, &TAG_ACTIVITY_SHOW, @(show),
OBJC_ASSOCIATION_RETAIN);
}

- (BOOL)sd_showActivityIndicatorView {
return [objc_getAssociatedObject(self, &TAG_ACTIVITY_SHOW) boolValue];
}

- (void)sd_setIndicatorStyle:(UIActivityIndicatorViewStyle)style{
objc_setAssociatedObject(self, &TAG_ACTIVITY_STYLE, [NSNumber
numberWithInt:style], OBJC_ASSOCIATION_RETAIN);
}

- (int)sd_getIndicatorStyle{
return [objc_getAssociatedObject(self, &TAG_ACTIVITY_STYLE) intValue];
}

- (void)sd_addActivityIndicator {
dispatch_main_async_safe(^{
if (!self.activityIndicator) {
self.activityIndicator = [[UIActivityIndicatorView alloc]
initWithActivityIndicatorStyle:[self sd_getIndicatorStyle]];
self.activityIndicator.translatesAutoresizingMaskIntoConstraints = NO;

[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]];
}
[self.activityIndicator startAnimating];
});
}

- (void)sd_removeActivityIndicator {
dispatch_main_async_safe(^{
if (self.activityIndicator) {
[self.activityIndicator removeFromSuperview];
self.activityIndicator = nil;
}
});
}

#endif

#endif

@end

You might also like