MJRefresh 中的 MJRefreshComponent , 面试官问我源代码,iOS 吹风指南:(无大段代码清爽版)...

如果是一名 iOS 开发者, 用 Objective-C,

  • 平时用的 mj_header , mj_footer, 到底是什么?
  • 是怎么给 mj_header , mj_footer, 添加方法的?

·····

平时给 tableView , collectionView 添加的 mj_footer , mj_header ,都是 MJRefreshComponent 的子类。

下拉刷新的框架设计,先搞定基础服务,就是 MJRefreshComponent 了。

MJRefreshComponent ,KVO + 状态管理,造类似系统的代理方法

MJRefreshComponent 本质上是一个 UIView, 添加了一个父视图对象 UIScrollView。然后需要把我们用的 mj_footer , mj_header 添加到父视图对象 scrollView 上面。 那个 scrollView 就是我们业务代码里面的 tableView , collectionView .

要想实现我们的下拉刷新,上拉加载, 就需要实现相关基础设施。对用户下拉和上拉作出反馈,MJRefreshComponent 采用的是观察三个属性。scrollView 的 contentOffset 和 contentSize ,scrollView 自带的平移手势 panGestureRecognizer 的 state.

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context

通过 KVO,把这三个属性转化为三个方法,

// 偏移量改变
- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change{}

// 内容大小改变
- (void)scrollViewContentSizeDidChange:(NSDictionary *)change{}

// 拖拽状态发生改变
- (void)scrollViewPanStateDidChange:(NSDictionary *)change{}

复制代码

Header 只调用 scrollViewContentOffsetDidChange:, Footer 调用上面三个方法。 Header 只关注 偏移量, Footer 都要关注。

相当于 MJ 使用 KVO 生造了三个类似系统 scrollView 的代理方法,出来了。

MJRefresh 处理的相当有节操, 直接采用 KVO , 不涉及任何的 scrollView 的系统代理方法。 非常的框架。不打扰业务代码的可能实现。

MJRefreshComponent 里面有一个内部方法 - (void)executeRefreshingCallback , 处理我们传入的事件,业务上一般是网络请求方法,上拉刷新第一页的数据,下拉加载下一页的数据。

可以检测到用户的操作了, 就要作出响应,显示 UI ,出现一朵菊花,执行我们的业务代码(作为外部传入给框架的)。

KVO 检测到的 scrollView 的偏移量 contentOffset, 内容大小 contentSize 是不断变化的,一触发, 啪啪啪, 一打数据甩过去了。

我们需要的是,在合适的时机,调用一次方法就好了。

MJRefreshComponent 采用的是状态管理。

/** 刷新控件的状态 */
typedef NS_ENUM(NSInteger, MJRefreshState) {
    MJRefreshStateIdle = 1,      /** 普通闲置状态 */    
    MJRefreshStatePulling,       /** 松开就可以进行刷新的状态 */
    MJRefreshStateRefreshing,     /** 正在刷新中的状态 */
    MJRefreshStateWillRefresh,    /** 即将刷新的状态 */
    MJRefreshStateNoMoreData     /** 所有数据加载完毕,没有更多的数据了 */
};

复制代码

把监测到的 scrollView 的偏移量和内容大小的变化,转化为状态的改变,把连续的数据变化转化为离散的一两次操作,挺不错的。具体在子类头部刷新(下拉刷新)MJRefreshHeader 和尾巴刷新(上拉加载)MJRefreshFooter 中实现。

执行开发者的操作 (一般是网络请求)

检测到用户的列表滚动情况了,可以转化为操作了,就要显示UI, 放在 MJRefreshComponent 的子类头部/尾部类中实现了。然后就是执行开发者传入的方法。

这个内部方法 - (void)executeRefreshingCallback ,提供两种实现,一种是匿名函数,self.refreshingBlock(); , 另一种是 target - action, MJ 封装了一个运行时的发消息方法 MJRefreshMsgSend(MJRefreshMsgTarget(self.refreshingTarget), self.refreshingAction, self);

继承,MJRefreshComponent 作为父类,提供基础设施,Header 和 Footer 具体功能,更进一步的子类提供样式 ( UI )

MJRefresh 在继承上使用的比较出彩,MJRefreshComponent 建立基础设施,MJRefreshHeader 完成了下拉加载的功能,MJRefreshStateHeader 添加了基本的样式(主要是文本标签 UILabel , 更新的时间文本, 状态文本), MJRefreshNormalHeader 添加了箭头和菊花(活动指示器,UIActivityIndicatorView )

具体上拉加载和下拉刷新的 UI 部分,MJRefreshComponent 提供了大致如下四个空方法实现。

/** 摆放子控件frame */
- (void)placeSubviews NS_REQUIRES_SUPER;

/** 当scrollView的contentOffset发生改变的时候调用 */
- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change NS_REQUIRES_SUPER;

/** 当scrollView的contentSize发生改变的时候调用 */
- (void)scrollViewContentSizeDidChange:(NSDictionary *)change NS_REQUIRES_SUPER;

/** 当scrollView的拖拽状态发生改变的时候调用 */
- (void)scrollViewPanStateDidChange:(NSDictionary *)change NS_REQUIRES_SUPER;
复制代码

MJRefresh 在继承上有两步干得漂亮, 从 MJRefreshComponent 到 Header 和 Footer , 这里有一个区分。

还有就是 MJMJRefresh 有很多的样式,比较有特色的是上拉刷新部分, 会回弹到底部的默认的 footer , 会回弹到底部的带动图的 footer , 会自动刷新的默认的 footer ,会自动刷新的带动图的 footer.

内存管理上,

MJRefreshComponent 在内存管理上,也有优点,

@interface MJRefreshComponent: UIView
{
    /** 父控件 */
    __weak UIScrollView * _scrollView;
}

/** 父控件 */
@property (weak, nonatomic, readonly) UIScrollView *scrollView;

复制代码

父视图 scrollView,我们的表视图,格子视图,外部 readonly 属性调用, 内部的 _scrollView 成员变量修改。

Runtime 上, 给系统类 scrollView 动态添加属性
- (void)setMj_footer:(MJRefreshFooter *)mj_footer{
    if (mj_footer != self.mj_footer) {
        // 删除旧的,添加新的
        [self.mj_footer removeFromSuperview];
        [self insertSubview:mj_footer atIndex:0];
        // 存储新的
        objc_setAssociatedObject(self, &MJRefreshFooterKey, mj_footer, OBJC_ASSOCIATION_RETAIN);
    }
}


- (MJRefreshFooter *)mj_footer{
    return objc_getAssociatedObject(self, &MJRefreshFooterKey);
}


复制代码

这一点上,NSHipster 的对象关联 Associated Objects 讲得很不错。

End

MJRefresh 对设计模式中继承, 观察者的使用, 使用继承的设计模式,分层加功能, 对 Runtime 的使用,对宏的大量使用, 都挺精彩的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值