各种分类:以UIView+WebCache为例分析
-
UIButton+WebCache
为UIButton类添加载图片的方法。比如正常情况下、点击情况下、的image属性和背景图片等。 -
MKAnnotationView+WebCache
为MKAnnotationView类添加各种加载图片的方法。 -
UIImageView+WebCache
为UIImageView类添加加载图片的方法。 -
UIImageView+HighlightedWebCache
为UIImageView类添加高亮状态下加载图片的方法。 -
FLAnimatedImageView+WebCache
为FLAnimatedImageView类添加加载动态的方法,这个分类需要引入 -
FLAnimatedImage
框架。SDWebImage推荐使用这个框架来处理动态图片(GIF)的加载。
UIImageView
、UIButton
、FLAnimatedImageView
都会调用UIView(WebCache)
分类的sd_internalSetImageWithURL
方法来做图片加载请求。具体是通过SDWebImageManager
调用来实现的。同时实现了Operation取消
、ActivityIndicator
的添加与取消。我们首先来看 sd_internalSetImageWithURL
方法的实现:
/**
所有UIView及其子类都是通过这个方法来加载图片
@param url 加载的url
@param placeholder 占位图
@param options 加载选项
@param operationKey key
@param setImageBlock Block
@param progressBlock 进度Block
@param completedBlock 回调Block
*/
- (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 {
//取消当前类所对应的所有下载Operation对象
NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
[self sd_cancelImageLoadOperationWithKey:validOperationKey];
/*
把UIImageView的加载图片操作和他自身用关联对象关联起来,方便后面取消等操作。关联的key就是UIImageView对应的类名
*/
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
//如果UIImageView对象有设置添加转动菊花数据,加载的时候添加转动的菊花
if ([self sd_showActivityIndicatorView]) {
[self sd_addActivityIndicator];
}
__weak __typeof(self)wself = self;
/*
*operation是一个`SDWebImageCombinedOperation`对象。通过这个对象来获取图片
*/
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 sd_removeActivityIndicator];
if (!sself) {
return;
}
dispatch_main_async_safe(^{
if (!sself) {
return;
}
//如果设置了不自动显示图片,则直接调用completedBlock,让调用者处理图片的显示
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);
}
});
}];
//关联Operationkey与Operation对象。方便后面根据key取消operation操作等。
[self sd_setImageLoadOperation:operation forKey:validOperationKey];
} else {
//加载失败的情况
dispatch_main_async_safe(^{
//移除菊花
[self sd_removeActivityIndicator];
if (completedBlock) {
NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
completedBlock(nil, error, SDImageCacheTypeNone, url);
}
});
}
}
给UIView及其子类添加旋转菊花是通过关联对象来实现的。通过如下几个方法来实现:
#pragma mark 通过关联对象来实现菊花的添加
- (UIActivityIndicatorView *)activityIndicator {
return (UIActivityIndicatorView *)objc_getAssociatedObject(self, &TAG_ACTIVITY_INDICATOR);
}
- (void)setActivityIndicator:(UIActivityIndicatorView *)activityIndicator {
objc_setAssociatedObject(self, &TAG_ACTIVITY_INDICATOR, activityIndicator, OBJC_ASSOCIATION_RETAIN);
}
#pragma mark 是否显示旋转菊花
- (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];
}
#pragma mark 旋转菊花的样式
- (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];
}
还有就是通过UIView+WebCacheOperation
类来实现UIView的图片下载Operation的关联和取消。具体key的值可以从sd_internalSetImageWithURL
中找到具体获取方式,通过在这个方法中实现 Operation的关联与取消。
/**
关联Operation对象与key对象
@param operation Operation对象
@param key key
*/
- (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key {
if (key) {
[self sd_cancelImageLoadOperationWithKey:key];
if (operation) {
SDOperationsDictionary *operationDictionary = [self operationDictionary];
operationDictionary[key] = operation;
}
}
}
/**
取消当前key对应的所有实现了SDWebImageOperation协议的Operation对象
@param key Operation对应的key
*/
- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key {
// Cancel in progress downloader from queue
//获取当前View对应的所有key
SDOperationsDictionary *operationDictionary = [self operationDictionary];
//获取对应的图片加载Operation
id operations = operationDictionary[key];
//取消所有当前View对应的所有Operation
if (operations) {
if ([operations isKindOfClass:[NSArray class]]) {
for (id <SDWebImageOperation> operation in operations) {
if (operation) {
[operation cancel];
}
}
} else if ([operations conformsToProtocol:@protocol(SDWebImageOperation)]){
[(id<SDWebImageOperation>) operations cancel];
}
[operationDictionary removeObjectForKey:key];
}
}
网友评论