SDWebImage之UIView+WebCache
UIView+WebCache是我们能很方便的使用sd_setImageWithURL:系列方法来加载图片的关键类。UIButton(WebCache)、MKAnnotationView(WebCache)、UIImageView(HighlightedWebCache)、FLAnimatedImageView(WebCache)都会调用UIView(WebCache)的sd_internalSetImageWithURL:
方法来做图片加载请求,实现了视图本身加载图片的方式。它和 UIView+WebCacheOperation
配合使用。下面我们来看一下它的实现。
1.核心方法
/** * 所有UIView及其子类都是通过这个方法来加载图片 * * 异步下载和缓存 * * @param url 图片URL * @param placeholder 占位图 * @param options 加载选项 * @param operationKey 操作(operation)的 key,如果为空时,将使用类名。这个主要使用来取消一个 operation,结合 UIView+WebCacheOperation.h 使用 * @param setImageBlock 如果不想使用 SD 加载完图片后显示到视图上,可以使用这个 Block 自定义加载图片,这样就可以在调用加载图片的方法中加载图片。 * @param progressBlock 进度回调 * @param completedBlock 图片加载完成后的回调 */ - (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]); //取消当前类对应的所有下载Operation对象 [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) { //如果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); } }); } } /** 取消当前Class对应的所有加载请求 */ - (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); return; } #if SD_UIKIT || SD_MAC if ([self isKindOfClass:[UIImageView class]]) { UIImageView *imageView = (UIImageView *)self; imageView.image = image; } #endif #if SD_UIKIT if ([self isKindOfClass:[UIButton class]]) { UIButton *button = (UIButton *)self; [button setImage:image forState:UIControlStateNormal]; } #endif }
2.旋转的菊花
#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 #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]; } #if SD_UIKIT #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]; } #endif - (void)sd_addActivityIndicator { #if SD_UIKIT 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]; }); #endif } - (void)sd_removeActivityIndicator { #if SD_UIKIT dispatch_main_async_safe(^{ if (self.activityIndicator) { [self.activityIndicator removeFromSuperview]; self.activityIndicator = nil; } }); #endif }
无善无恶心之体,
有善有恶意之动,
知善知恶是良知,
为善去恶是格物。