第三方库之-MJRefresh

MJRefresh类的关系图


研究了一下MJRefresh框架, 整体的结构大概是这样的, mj_header和mj_footer都继承自一个基类MJRefreshComponent, 这个基类继承自UIView, mj_header和mj_footer又都有本身的子类, 子类来实现具体的方法.git

框架中还有一个UIScrollView的分类UIScrollView+MJRefresh. 这个分类是一个比较简单的分类, 包含三个属性:mj_header, mj_footer, mj_reloadDataBlock, 和一个方法- (NSInteger)mj_totalDataCount, 这个方法能够用来获取UITableView和UICollectionView的包含数据的条目数量.github

#1. MJRefresh学习笔记web

1.1 利用objc/runtime交换方法的实现

以下代码所示, 实现的功能是, 交换两个方法的实现, 利用runtime的原理.框架

@implementation NSObject (MJRefresh)
// 交换两个实例方法的实现
+ (void)exchangeInstanceMethod1:(SEL)method1 method2:(SEL)method2
{
    method_exchangeImplementations(class_getInstanceMethod(self, method1), class_getInstanceMethod(self, method2));
}

// 交换两个类方法的实现
+ (void)exchangeClassMethod1:(SEL)method1 method2:(SEL)method2
{
    method_exchangeImplementations(class_getClassMethod(self, method1), class_getClassMethod(self, method2));
}

@end

1.2 获取UITableView和UICollectionView数据的数量

- (NSInteger)mj_totalDataCount
{
    NSInteger totalCount = 0;
    if ([self isKindOfClass:[UITableView class]]) {
        UITableView *tableView = (UITableView *)self;
        
        for (NSInteger section = 0; section<tableView.numberOfSections; section++) {
            totalCount += [tableView numberOfRowsInSection:section];
        }
    } else if ([self isKindOfClass:[UICollectionView class]]) {
        UICollectionView *collectionView = (UICollectionView *)self;
        
        for (NSInteger section = 0; section<collectionView.numberOfSections; section++) {
            totalCount += [collectionView numberOfItemsInSection:section];
        }
    }
    return totalCount;
}

1.3 mj_header的set,get方法 注释版

#pragma mark - header
static const char MJRefreshHeaderKey = '\0';
- (void)setMj_header:(MJRefreshHeader *)mj_header
{
    if (mj_header != self.mj_header) {
        // 删除旧的,添加新的
        // 当添加新的头刷新控件时候, 先移除旧的头部刷新控件
        [self.mj_header removeFromSuperview];
        // 插入新的头部刷新控件,, 使用insertSubview:atIndex:, 将控件插入到指定的位置, 方便后面去查找这个控件, 摆放控件的位置.
        [self insertSubview:mj_header atIndex:0];
        
        // 存储新的
        // 使用关联(association)来给对象动态的添加一个关联对象.  willChangeValueForKey: 和 didChangeValueForKey: 是为了触发KVO, 可能在其它地方监听了这个关联的变化, 以从新布局呀, 从新刷新呀之类的工做.
        [self willChangeValueForKey:@"mj_header"]; // KVO
        objc_setAssociatedObject(self, &MJRefreshHeaderKey,
                                 mj_header, OBJC_ASSOCIATION_ASSIGN);
        [self didChangeValueForKey:@"mj_header"]; // KVO
    }
}

- (MJRefreshHeader *)mj_header
{
    // association的get方法, 根据实例对象和key获取一个关联对象.
    return objc_getAssociatedObject(self, &MJRefreshHeaderKey);
}

1.4 三目运算符还能够这么用

规则的三目运算符是 [表达式1]?[表达式2]:[表达式3], 其它语言没有测试, oc中也能够这么用: [表达式1]? : [表达式3]布局

!self.mj_reloadDataBlock ? : self.mj_reloadDataBlock(self.mj_totalDataCount);

1.5 MJRefresh中UICollectionView的分类是这么写的

@implementation UICollectionView (MJRefresh)
// load是一个初始化方法, 在类刚刚开始初始化时候调用, 在类刚开始初始化时候调用这个方法, 交换两个方法的实现
+ (void)load
{
    [self exchangeInstanceMethod1:@selector(reloadData) method2:@selector(mj_reloadData)];
}

// load时候交换了这两个方法的实现, 因此在这个方法里面能够调用本身, 看起来是调用本身, 其实调用了UITableView的reloadData方法
// 这里的逻辑是这样的, 咱们刷新tableView数据时候调用reloadData, 实际上会调用到mj_reloadData方法, 这个方法的实现调用mj_reloadData实际上会调用到系统的reloadData方法, 而后执行 UIScrollView的executeReloadDataBlock方法, 来执行一个block代码块
- (void)mj_reloadData
{
    [self mj_reloadData];
    
    [self executeReloadDataBlock];
}
@end

1.6 执行某一个对象的一个方法

MJRefresh中使用的一个宏定义:
// 运行时objc_msgSend
#define MJRefreshMsgSend(...) ((void (*)(void *, SEL, UIView *))objc_msgSend)(__VA_ARGS__)
#define MJRefreshMsgTarget(target) (__bridge void *)(target)

使用:
/**
* 其中 self.refreshingTarget表示要执行的方法所在的对象,id类型; self.refreshingAction表示要执行的方法,SEL类型; self表示当前的view.
* 简单来讲就是执行target的action方法.
*/
MJRefreshMsgSend(MJRefreshMsgTarget(self.refreshingTarget), self.refreshingAction, self)

1.7 UIScrollView的一个属性:scrollView.isDragging

经过scrollView的这个属性能够判断出来scrollView当前是否是正在被拖拽.学习

self.scrollView.isDragging

1.8 UIWebView下拉刷新

- (void)example31
{
    __unsafe_unretained UIWebView *webView = self.webView;
    webView.delegate = self;
    
    __unsafe_unretained UIScrollView *scrollView = self.webView.scrollView;
    
    // 添加下拉刷新控件
    scrollView.mj_header= [MJRefreshNormalHeader headerWithRefreshingBlock:^{
        [webView reload];
    }];
    
    // 若是是上拉刷新,就以此类推
}

1.9 设置状态栏的样式和是否隐藏

/**
 *  描述: 状态栏样式
 *
 *  @return 返回状态栏的样式
 */
- (UIStatusBarStyle)preferredStatusBarStyle
{
    return self.statusBarStyle;
}

/**
 *  描述: 状态栏是否隐藏
 *
 *  @return 返回状态栏是否隐藏
 */
- (BOOL)prefersStatusBarHidden
{
    return self.statusBarHidden;
}

/**
 *  setter方法
 *
 *  @param statusBarHidden 是否隐藏的setter方法
 */
- (void)setStatusBarHidden:(BOOL)statusBarHidden
{
    _statusBarHidden = statusBarHidden;
    
    [self setNeedsStatusBarAppearanceUpdate];
}

/**
 *  setter方法
 *
 *  @param statusBarStyle 状态栏样式的setter方法
 */
- (void)setStatusBarStyle:(UIStatusBarStyle)statusBarStyle
{
    _statusBarStyle = statusBarStyle;
    
    [self setNeedsStatusBarAppearanceUpdate];
}

2. 谈谈阅读MJRefresh框架的感觉

上下拉刷新的实现原理测试

上下拉刷新的实现仍是比较简单的. 下拉刷新的header, 实际上, header的位置的y坐标是负值, 这就致使tableView中正常状况下是不会显示出来header的, 当下拉时候, header就会被展现出来, 当下拉到必定程度, 超过header的height时候, 再松手会立刻改变tableView的inset并从新放header的位置, 切换上面的数据, endRefreshing时候, 会从新改变tableviw的inset, 执行动画将header隐藏掉. 上拉加载的原理和这个如出一辙.动画

MJRefresh第一次阅读设计

这个上下拉刷新的框架, 目前在github上有七千多的star, 对中国区的iOS开发者来讲, 是一个很不错的框架. 阅读起来也是没有太大难度的, 可是经过阅读这个开源框架仍是学到了不少的东西.code

首先, 经过继承来封装一个框架, 可让框架业务逻辑层级分明, 更易于维护. 之前我也有了解过模板方法模式, 简单说就是经过继承实现复用, 今天阅读完MJRefresh框架, 我想对模板方法模式又有了新的认识和理解. 在框架的设计和封装时候, 咱们能够在基础的父类中实现最基本的业务逻辑, 定义抽象类, 而后让子类去处理方法的实现. 若是方法的实现很是的复杂, 能够利用相似MJRefresh中, 处于不一样层次的子类实现本身不一样的业务, 而后再去调用父类的方法, 这样达到具体业务处理和逻辑处理的分离, 让代码更加便于维护和阅读.

其次, 封装一个框架时候注意事项. 我的认为: 1.尽量的下降程序每一个部分的耦合度. 2.要写易于扩展和维护的代码. 3.无论程序内部实现多么复杂, 抛出来的接口必定要尽量的简单.

其次阅读优秀框架, 可以很好的提高我的的水平, 也能提高程序猿们重造轮子的能力, 是不 ?

MJRefresh注释版

相关文章
相关标签/搜索