前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >AFNetworking源码探究(十九) —— UIKit相关之几个分类

AFNetworking源码探究(十九) —— UIKit相关之几个分类

原创
作者头像
conanma
修改2021-09-03 18:00:51
4520
修改2021-09-03 18:00:51
举报
文章被收录于专栏:正则正则

回顾

上一篇主要讲述了AFNetworkActivityIndicatorManager数据加载指示器。这一篇主要讲述了UIKit的几个分类。


UIActivityIndicatorView+AFNetworking

1. 接口调用

这个是指示器的一个分类,看一下.h接口。

代码语言:javascript
复制
@interface UIActivityIndicatorView (AFNetworking)

///----------------------------------
/// @name Animating for Session Tasks
///----------------------------------

/**
 Binds the animating state to the state of the specified task.

 @param task The task. If `nil`, automatic updating from any previously specified operation will be disabled.
 */
- (void)setAnimatingWithStateOfTask:(nullable NSURLSessionTask *)task;

@end

将动画状态绑定到指定任务的状态。这里的参数Task,如果为nil,则从先前指定的操作的自动更新将被禁用。

这里只有上面一个暴露出来的方法。

下面我们在看下.m里面的实现,这里多了一个类AFActivityIndicatorViewNotificationObserver,实现过程如下:

代码语言:javascript
复制
#import "UIActivityIndicatorView+AFNetworking.h"
#import <objc/runtime.h>

#if TARGET_OS_IOS || TARGET_OS_TV

#import "AFURLSessionManager.h"

@interface AFActivityIndicatorViewNotificationObserver : NSObject
@property (readonly, nonatomic, weak) UIActivityIndicatorView *activityIndicatorView;
- (instancetype)initWithActivityIndicatorView:(UIActivityIndicatorView *)activityIndicatorView;

- (void)setAnimatingWithStateOfTask:(NSURLSessionTask *)task;

@end

@implementation UIActivityIndicatorView (AFNetworking)

- (AFActivityIndicatorViewNotificationObserver *)af_notificationObserver {
    AFActivityIndicatorViewNotificationObserver *notificationObserver = objc_getAssociatedObject(self, @selector(af_notificationObserver));
    if (notificationObserver == nil) {
        notificationObserver = [[AFActivityIndicatorViewNotificationObserver alloc] initWithActivityIndicatorView:self];
        objc_setAssociatedObject(self, @selector(af_notificationObserver), notificationObserver, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return notificationObserver;
}

- (void)setAnimatingWithStateOfTask:(NSURLSessionTask *)task {
    [[self af_notificationObserver] setAnimatingWithStateOfTask:task];
}

@end

@implementation AFActivityIndicatorViewNotificationObserver

- (instancetype)initWithActivityIndicatorView:(UIActivityIndicatorView *)activityIndicatorView
{
    self = [super init];
    if (self) {
        _activityIndicatorView = activityIndicatorView;
    }
    return self;
}

- (void)setAnimatingWithStateOfTask:(NSURLSessionTask *)task {
    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];

    [notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil];
    [notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil];
    [notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil];
    
    if (task) {
        if (task.state != NSURLSessionTaskStateCompleted) {
            UIActivityIndicatorView *activityIndicatorView = self.activityIndicatorView;
            if (task.state == NSURLSessionTaskStateRunning) {
                [activityIndicatorView startAnimating];
            } else {
                [activityIndicatorView stopAnimating];
            }

            [notificationCenter addObserver:self selector:@selector(af_startAnimating) name:AFNetworkingTaskDidResumeNotification object:task];
            [notificationCenter addObserver:self selector:@selector(af_stopAnimating) name:AFNetworkingTaskDidCompleteNotification object:task];
            [notificationCenter addObserver:self selector:@selector(af_stopAnimating) name:AFNetworkingTaskDidSuspendNotification object:task];
        }
    }
}

#pragma mark -

- (void)af_startAnimating {
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.activityIndicatorView startAnimating];
    });
}

- (void)af_stopAnimating {
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.activityIndicatorView stopAnimating];
    });
}

#pragma mark -

- (void)dealloc {
    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
    
    [notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil];
    [notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil];
    [notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil];
}

@end

#endif

2. 接口方法的转发实现

接下里我们就从那个暴露出来的方法,来看这个分类的实现。

代码语言:javascript
复制
// AFActivityIndicatorViewNotificationObserver实例化,
// 并利用runtime绑定key对应的value
- (AFActivityIndicatorViewNotificationObserver *)af_notificationObserver {
    AFActivityIndicatorViewNotificationObserver *notificationObserver = objc_getAssociatedObject(self, @selector(af_notificationObserver));
    if (notificationObserver == nil) {
        notificationObserver = [[AFActivityIndicatorViewNotificationObserver alloc] initWithActivityIndicatorView:self];
        // 四个参数 object/key/value/policy
        objc_setAssociatedObject(self, @selector(af_notificationObserver), notificationObserver, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return notificationObserver;
}

- (void)setAnimatingWithStateOfTask:(NSURLSessionTask *)task {
    // 调用了af_notificationObserver方法获取AFActivityIndicatorViewNotificationObserver的对象。
    [[self af_notificationObserver] setAnimatingWithStateOfTask:task];
}

这段代码主要做了下面几个工作:

  • 获取了AFActivityIndicatorViewNotificationObserver对象,并利用runtime绑定了key-value。
  • AFActivityIndicatorViewNotificationObserver对象的初始化。
  • 将接口那个方法转发到AFActivityIndicatorViewNotificationObserver中的方法了。

(a) AFActivityIndicatorViewNotificationObserver对象初始化

我们先看一下初始化

代码语言:javascript
复制
@property (readonly, nonatomic, weak) UIActivityIndicatorView *activityIndicatorView;

- (instancetype)initWithActivityIndicatorView:(UIActivityIndicatorView *)activityIndicatorView
{
    self = [super init];
    if (self) {
          // 只是简单的赋值
        _activityIndicatorView = activityIndicatorView;
    }
    return self;
}

这个初始化方法没什么特别的处理就是简单的赋值。

(b) 接口方法的转发

该分类的调用接口会转发到对象AFActivityIndicatorViewNotificationObserver中去实现。下面看一下是如何实现的。

代码语言:javascript
复制
- (void)setAnimatingWithStateOfTask:(NSURLSessionTask *)task {
    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];

    // 先移除对上个任务的通知监听
    [notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil];
    [notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil];
    [notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil];
    
    if (task) { 
        // 如果任务状态没有完成
        if (task.state != NSURLSessionTaskStateCompleted) {
            UIActivityIndicatorView *activityIndicatorView = self.activityIndicatorView;
            // 如果任务正在进行,就开始动画,否则就停止动画
            if (task.state == NSURLSessionTaskStateRunning) {
                [activityIndicatorView startAnimating];
            } else {
                [activityIndicatorView stopAnimating];
            }
            
            // 添加任务的开始暂停完成的通知
            [notificationCenter addObserver:self selector:@selector(af_startAnimating) name:AFNetworkingTaskDidResumeNotification object:task];
            [notificationCenter addObserver:self selector:@selector(af_stopAnimating) name:AFNetworkingTaskDidCompleteNotification object:task];
            [notificationCenter addObserver:self selector:@selector(af_stopAnimating) name:AFNetworkingTaskDidSuspendNotification object:task];
        }
    }
}

下面看一下这个方法的实现。

  • 首先移除对任务状态的监听,防止监听错误的任务到处出错。
  • 首先看一下任务的状态NSURLSessionTaskState,是一个枚举值,这个是苹果的API。
代码语言:javascript
复制
typedef NS_ENUM(NSInteger, NSURLSessionTaskState) {
    NSURLSessionTaskStateRunning = 0,                     /* The task is currently being serviced by the session */
    NSURLSessionTaskStateSuspended = 1,
    NSURLSessionTaskStateCanceling = 2,                   /* The task has been told to cancel.  The session will receive a URLSession:task:didCompleteWithError: message. */
    NSURLSessionTaskStateCompleted = 3,                   /* The task has completed and the session will receive no more delegate notifications */
} NS_ENUM_AVAILABLE(NSURLSESSION_AVAILABLE, 7_0);

如果任务没有完成,如果任务是在进行中,就开始动画,否则就停止动画。

  • 增加对任务开始、暂停和完成的通知监听。在通知方法的实现上就是在主线程进行开始或者停止动画。
代码语言:javascript
复制
- (void)af_startAnimating {
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.activityIndicatorView startAnimating];
    });
}

- (void)af_stopAnimating {
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.activityIndicatorView stopAnimating];
    });
}

最后在dealloc中移除对通知的监听。

后记

本篇主要是UIActivityIndicatorView的分类的一些动画的实现,将动画与任务的进度进行绑定。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 回顾
  • UIActivityIndicatorView+AFNetworking
    • 1. 接口调用
      • 2. 接口方法的转发实现
      • 后记
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档