博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
源码阅读:AFNetworking(十二)——UIButton+AFNetworking
阅读量:7073 次
发布时间:2019-06-28

本文共 15018 字,大约阅读时间需要 50 分钟。

该文章阅读的AFNetworking的版本为3.2.0。

这个分类是为UIButton添加异步加载网络图片的方法

1.接口文件

  • 图片下载器的访问方法
/** 设置用于下载图片的图片下载器*/+ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader;/** 获取用于下载图片的图片下载器 */+ (AFImageDownloader *)sharedImageDownloader;复制代码
  • 为按钮设置图片的方法
/** 为按钮设置指定状态和指定图片链接的图片 */- (void)setImageForState:(UIControlState)state                 withURL:(NSURL *)url;/** 为按钮设置指定状态、指定图片链接和指定占位图的图片 */- (void)setImageForState:(UIControlState)state                 withURL:(NSURL *)url        placeholderImage:(nullable UIImage *)placeholderImage;/** 为按钮设置指定状态、指定图片链接、指定占位图以及指定成功与失败回调block的图片 */- (void)setImageForState:(UIControlState)state          withURLRequest:(NSURLRequest *)urlRequest        placeholderImage:(nullable UIImage *)placeholderImage                 success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success                 failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure;复制代码
  • 为按钮设置背景图片的方法
/** 为按钮设置指定状态和指定图片链接的背景图片 */- (void)setBackgroundImageForState:(UIControlState)state                           withURL:(NSURL *)url;/** 为按钮设置指定状态、指定图片链接和指定占位图的背景图片 */- (void)setBackgroundImageForState:(UIControlState)state                           withURL:(NSURL *)url                  placeholderImage:(nullable UIImage *)placeholderImage;/** 为按钮设置指定状态、指定图片链接、指定占位图以及指定成功与失败回调block的背景图片 */- (void)setBackgroundImageForState:(UIControlState)state                    withURLRequest:(NSURLRequest *)urlRequest                  placeholderImage:(nullable UIImage *)placeholderImage                           success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success                           failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure;复制代码
  • 取消图片加载方法
/** 取消按钮在指定状态下的所有图片下载任务 */- (void)cancelImageDownloadTaskForState:(UIControlState)state;/** 取消按钮在指定状态下的所有背景图片下载任务 */- (void)cancelBackgroundImageDownloadTaskForState:(UIControlState)state;复制代码

2.实现文件

2.1.UIButton+_AFNetworking私有分类

2.1.1.image相关

  • 静态字符
// 普通static char AFImageDownloadReceiptNormal;// 高亮static char AFImageDownloadReceiptHighlighted;// 已选static char AFImageDownloadReceiptSelected;// 禁用static char AFImageDownloadReceiptDisabled;复制代码
  • 静态方法
/** 这个方法通过传入的控件状态返回对应的静态字符 */static const char * af_imageDownloadReceiptKeyForState(UIControlState state) {    switch (state) {        case UIControlStateHighlighted:            return &AFImageDownloadReceiptHighlighted;        case UIControlStateSelected:            return &AFImageDownloadReceiptSelected;        case UIControlStateDisabled:            return &AFImageDownloadReceiptDisabled;        case UIControlStateNormal:        default:            return &AFImageDownloadReceiptNormal;    }}复制代码
  • 属性的访问方法
/** 通过Runtime的关联对象为分类添加属性的getter */- (AFImageDownloadReceipt *)af_imageDownloadReceiptForState:(UIControlState)state {    return (AFImageDownloadReceipt *)objc_getAssociatedObject(self, af_imageDownloadReceiptKeyForState(state));}/** 通过Runtime的关联对象为分类添加属性的setter */- (void)af_setImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt                           forState:(UIControlState)state{    objc_setAssociatedObject(self, af_imageDownloadReceiptKeyForState(state), imageDownloadReceipt, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}复制代码

2.1.2.backgroundImage相关

  • 静态字符
// 普通static char AFBackgroundImageDownloadReceiptNormal;// 高亮static char AFBackgroundImageDownloadReceiptHighlighted;// 已选static char AFBackgroundImageDownloadReceiptSelected;// 禁用static char AFBackgroundImageDownloadReceiptDisabled;复制代码
  • 静态方法
/** 这个方法通过传入的控件状态返回对应的静态字符 */static const char * af_backgroundImageDownloadReceiptKeyForState(UIControlState state) {    switch (state) {        case UIControlStateHighlighted:            return &AFBackgroundImageDownloadReceiptHighlighted;        case UIControlStateSelected:            return &AFBackgroundImageDownloadReceiptSelected;        case UIControlStateDisabled:            return &AFBackgroundImageDownloadReceiptDisabled;        case UIControlStateNormal:        default:            return &AFBackgroundImageDownloadReceiptNormal;    }}复制代码
  • 属性的访问方法
/** 通过Runtime的关联对象为分类添加属性的getter */- (AFImageDownloadReceipt *)af_backgroundImageDownloadReceiptForState:(UIControlState)state {    return (AFImageDownloadReceipt *)objc_getAssociatedObject(self, af_backgroundImageDownloadReceiptKeyForState(state));}/** 通过Runtime的关联对象为分类添加属性的setter */- (void)af_setBackgroundImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt                                     forState:(UIControlState)state{    objc_setAssociatedObject(self, af_backgroundImageDownloadReceiptKeyForState(state), imageDownloadReceipt, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}复制代码

2.2.方法实现

  • 属性的访问方法
/** 通过Runtime的关联对象为分类添加sharedImageDownloader属性的getter */+ (AFImageDownloader *)sharedImageDownloader {    return objc_getAssociatedObject(self, @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance];}/** 通过Runtime的关联对象为分类添加sharedImageDownloader属性的setter */+ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader {    objc_setAssociatedObject(self, @selector(sharedImageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}复制代码
  • image相关接口方法实现
- (void)setImageForState:(UIControlState)state                 withURL:(NSURL *)url{    // 调用下面的方法    [self setImageForState:state withURL:url placeholderImage:nil];}- (void)setImageForState:(UIControlState)state                 withURL:(NSURL *)url        placeholderImage:(UIImage *)placeholderImage{    // 调用下面的方法    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];    [request addValue:@"image/*" forHTTPHeaderField:@"Accept"];    [self setImageForState:state withURLRequest:request placeholderImage:placeholderImage success:nil failure:nil];}- (void)setImageForState:(UIControlState)state          withURLRequest:(NSURLRequest *)urlRequest        placeholderImage:(nullable UIImage *)placeholderImage                 success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success                 failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure{    // 如果这个网络请求正在进行中就不重复执行了    if ([self isActiveTaskURLEqualToURLRequest:urlRequest forState:state]) {        return;    }    // 取消掉这个状态的图片下载任务    [self cancelImageDownloadTaskForState:state];    // 实例化图片下载器    AFImageDownloader *downloader = [[self class] sharedImageDownloader];    // 获取到图片缓存对象    id 
imageCache = downloader.imageCache; // 获取到缓存图片 UIImage *cachedImage = [imageCache imageforRequest:urlRequest withAdditionalIdentifier:nil]; // 如果有缓存 if (cachedImage) { // 如果设置了成功回调block就调用,否则就为按钮设置图片 if (success) { success(urlRequest, nil, cachedImage); } else { [self setImage:cachedImage forState:state]; } [self af_setImageDownloadReceipt:nil forState:state]; // 如果没有缓存 } else { // 如果有占位图先设置占位图 if (placeholderImage) { [self setImage:placeholderImage forState:state]; } // 开始下载 __weak __typeof(self)weakSelf = self; NSUUID *downloadID = [NSUUID UUID]; AFImageDownloadReceipt *receipt; receipt = [downloader downloadImageForURLRequest:urlRequest withReceiptID:downloadID success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) { // 如果是当前按钮的下载回调 __strong __typeof(weakSelf)strongSelf = weakSelf; if ([[strongSelf af_imageDownloadReceiptForState:state].receiptID isEqual:downloadID]) { // 如果设置了成功回调block就调用,否则就为按钮设置图片 if (success) { success(request, response, responseObject); } else if(responseObject) { [strongSelf setImage:responseObject forState:state]; } // 将保存下载封装对象的属性置nil [strongSelf af_setImageDownloadReceipt:nil forState:state]; } } failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) { // 如果是当前按钮的下载回调 __strong __typeof(weakSelf)strongSelf = weakSelf; if ([[strongSelf af_imageDownloadReceiptForState:state].receiptID isEqual:downloadID]) { // 失败回调block if (failure) { failure(request, response, error); } // 将保存下载封装对象的属性置nil [strongSelf af_setImageDownloadReceipt:nil forState:state]; } }]; // 用分类的属性保存图片下载封装对象 [self af_setImageDownloadReceipt:receipt forState:state]; }}- (void)cancelImageDownloadTaskForState:(UIControlState)state { // 通过状态获取到图片下载封装对象 AFImageDownloadReceipt *receipt = [self af_imageDownloadReceiptForState:state]; if (receipt != nil) { // 根据获取到的图片下载封装对象取消对应的图片下载任务 [[self.class sharedImageDownloader] cancelTaskForImageDownloadReceipt:receipt]; // 把保存图片下载封装对象的属性置nil [self af_setImageDownloadReceipt:nil forState:state]; }}复制代码
  • backgroundImage相关接口方法实现

backgroundImage的实现和image的实现除了在设置图片时一个给backgroundImage,一个给image之外都相同。

- (void)setBackgroundImageForState:(UIControlState)state                           withURL:(NSURL *)url{    [self setBackgroundImageForState:state withURL:url placeholderImage:nil];}- (void)setBackgroundImageForState:(UIControlState)state                           withURL:(NSURL *)url                  placeholderImage:(nullable UIImage *)placeholderImage{    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];    [request addValue:@"image/*" forHTTPHeaderField:@"Accept"];    [self setBackgroundImageForState:state withURLRequest:request placeholderImage:placeholderImage success:nil failure:nil];}- (void)setBackgroundImageForState:(UIControlState)state                    withURLRequest:(NSURLRequest *)urlRequest                  placeholderImage:(nullable UIImage *)placeholderImage                           success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success                           failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure{    if ([self isActiveBackgroundTaskURLEqualToURLRequest:urlRequest forState:state]) {        return;    }    [self cancelBackgroundImageDownloadTaskForState:state];    AFImageDownloader *downloader = [[self class] sharedImageDownloader];    id 
imageCache = downloader.imageCache; //Use the image from the image cache if it exists UIImage *cachedImage = [imageCache imageforRequest:urlRequest withAdditionalIdentifier:nil]; if (cachedImage) { if (success) { success(urlRequest, nil, cachedImage); } else { [self setBackgroundImage:cachedImage forState:state]; } [self af_setBackgroundImageDownloadReceipt:nil forState:state]; } else { if (placeholderImage) { [self setBackgroundImage:placeholderImage forState:state]; } __weak __typeof(self)weakSelf = self; NSUUID *downloadID = [NSUUID UUID]; AFImageDownloadReceipt *receipt; receipt = [downloader downloadImageForURLRequest:urlRequest withReceiptID:downloadID success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) { __strong __typeof(weakSelf)strongSelf = weakSelf; if ([[strongSelf af_backgroundImageDownloadReceiptForState:state].receiptID isEqual:downloadID]) { if (success) { success(request, response, responseObject); } else if(responseObject) { [strongSelf setBackgroundImage:responseObject forState:state]; } [strongSelf af_setBackgroundImageDownloadReceipt:nil forState:state]; } } failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) { __strong __typeof(weakSelf)strongSelf = weakSelf; if ([[strongSelf af_backgroundImageDownloadReceiptForState:state].receiptID isEqual:downloadID]) { if (failure) { failure(request, response, error); } [strongSelf af_setBackgroundImageDownloadReceipt:nil forState:state]; } }]; [self af_setBackgroundImageDownloadReceipt:receipt forState:state]; }}- (void)cancelBackgroundImageDownloadTaskForState:(UIControlState)state { AFImageDownloadReceipt *receipt = [self af_backgroundImageDownloadReceiptForState:state]; if (receipt != nil) { [[self.class sharedImageDownloader] cancelTaskForImageDownloadReceipt:receipt]; [self af_setBackgroundImageDownloadReceipt:nil forState:state]; }}复制代码
  • 私有方法
/** 判断图片下载请求是否已经正在进行中 */- (BOOL)isActiveTaskURLEqualToURLRequest:(NSURLRequest *)urlRequest forState:(UIControlState)state {    // 先根据状态获取到属性中保存的图片下载封装对象    AFImageDownloadReceipt *receipt = [self af_imageDownloadReceiptForState:state];    // 根据两者的链接是否相同进行比较    return [receipt.task.originalRequest.URL.absoluteString isEqualToString:urlRequest.URL.absoluteString];}/** 判断背景图片下载请求是否已经正在进行中 */- (BOOL)isActiveBackgroundTaskURLEqualToURLRequest:(NSURLRequest *)urlRequest forState:(UIControlState)state {    // 实现和上面的方法相同    AFImageDownloadReceipt *receipt = [self af_backgroundImageDownloadReceiptForState:state];    return [receipt.task.originalRequest.URL.absoluteString isEqualToString:urlRequest.URL.absoluteString];}复制代码

3.总结

当我们利用这个分类为按钮设置图片时

  • 首先会根据要设置的控件状态和图片链接判断是否是重复下载,如果是重复下载就不继续向下进行了。
  • 接着会取消在设置的控件状态下正在进行的下载操作(如果有的话),也就是说只有最新的这次图片设置才有效。
  • 然后获取到图片下载器的图片缓存对象,从图片缓存对象中查找是否有缓存,如果有缓存的话会直接取出添加到按钮上,但是如果用户设置了成功回调block,就不会将图片添加到按钮上,而是会调用blokc,将结果传递给用户,让用户来处理。
  • 如果没有缓存,就会先判断有没有占位图,如果有占位图的话,就先给按钮设置占位图。
  • 接下来将UUID作为标识符开启图片下载任务,并将任务封装对象保存在属性中,属性的命名是根据图片要设置的控件状态相关联的。
  • 下载任务执行完成后,无论成功还是失败,都会先根据设置的控件状态获取到对应的下载任务封装对象,对比下载任务封装对象的标识符和UUID判断是否是同一任务的回调,只用是同一任务的回调才继续执行
  • 在下载成功的情况下,如果用户设置了成功回调block,就调用block,将结果传递给用户,让用户来处理,如果没有设置,就直接将下载好的图片添加到按钮的指定状态上,并将指定状态所对应的保存下载任务封装对象的属性置nil。
  • 当下载失败时,如果用户设置了失败回调block,就调用block,将结果传递给用户,让用户来处理,如果没有设置,就将指定状态所对应的保存下载任务封装对象的属性置nil。

源码阅读系列:AFNetworking

转载地址:http://eehll.baihongyu.com/

你可能感兴趣的文章
mysql中的锁
查看>>
Android的消息机制(1)
查看>>
Vue中v-for的数据分组
查看>>
ajax 无刷新下拉加载更多。
查看>>
linux运维人员常用的150个命令
查看>>
bzoj3068: 小白树
查看>>
常用算法Java实现之直接插入排序
查看>>
转载 radio值获取
查看>>
学习SpringMVC——你们要的REST风格的CRUD来了
查看>>
NLPIR数据语义挖掘技术为企业提供精准管理
查看>>
通过本地yum源安装软件报错[Errno 14] PYCURL ERROR 56 - "Failure when receiving data from the peer"...
查看>>
android常用调试工具fiddle、wireshark和android studio的配置
查看>>
Java实现几种常见排序方法
查看>>
NOIP2017 复盘
查看>>
jxa快速入门,Javascript已加入AppleScript全家桶
查看>>
洛谷P3622 动物园
查看>>
Angular Encapsulation - css选择器选不到非angular组件(插件)
查看>>
iOS开发之UIScrollView
查看>>
mysql 使用 insert ignore into和unique实现不插入重复数据功能
查看>>
c++操作符重载_12
查看>>