博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
所闻所获3:下拉刷新控件1
阅读量:5172 次
发布时间:2019-06-13

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

  本文主要是讨论在最近项目中遇到的一个下拉刷新控件,这个控件的效果如下图:

 

  在这里会用两篇博文的篇幅来解析这个控件,第一篇解析控件的框架,第二篇解析动画。源代码可以在下面的链接下载:

 

 

1、这个控件由以下几个文件组成:GMPullToAction、CircleProgressView、GMActivityView,其中GMPullToAction文件包含两个类:GMPullToRefresh和UIScrollView (GMPullToAction),CircleProgressView和GMActivityView各自包含一个同名的类。

  在这4个类中,GMPullToRefresh和UIScrollView (GMPullToAction)是控件的框架,CircleProgressView和GMActivityView负责动画。

 

2、这个控件定义在UIScrollView (GMPullToAction)内,所以使用方必须是UIScrollView或者它的子类的实例,使用时需要调用3个方法(假设当前控制器self有一属性scrollView):

[self.scrollView addPullToRefreshWithActionHandler:^{    //下拉刷新时执行的代码    ...}];

  然后需要实现UIScrollViewDelegate的一个代理方法:

//在scrollView拖动的时候会不断调用这个方法- (void)scrollViewDidScroll:(UIScrollView *)scrollView {    if (scrollView == self.scrollView) {        [scrollView didScroll];    }}

  最后在加载完成后,还要调用以下这个方法来停用控件:

[self.scrollView.pullToRefreshView stopAnimating];

  我们就以这3个方法为入口来解析这个控件。

 

3、首先来看第一个方法- (void)addPullToRefreshWithActionHandler:(void (^)(void))actionHandler方法:

(1)、这个方法定义在UIScrollView (GMPullToAction)类中,它的代码如下:

- (void)addPullToRefreshWithActionHandler:(void (^)(void))actionHandler {    GMPullToRefresh *pullToRefreshView = [[GMPullToRefresh alloc] initWithScrollView:self];    pullToRefreshView.actionHandler = actionHandler;    self.pullToRefreshView = pullToRefreshView;}

  它使用initWithScrollView方法实例化了一个GMPullToRefresh类的对象pullToRefreshView,将块参数指定给pullToRefreshView.actionHandler,让pullToRefreshView可以在后续使用这段代码,最后把pullToRefreshView指定给自己的属性self.pullToRefreshView。

  这里需要注意一下,UIScrollView (GMPullToAction)类是一个分类,是不允许直接定义属性的,所以需要用到另外的方法来实现属性的效果,具体代码如下:

@interface UIScrollView (GMPullToAction)@property (nonatomic, strong) GMPullToRefresh *pullToRefreshView;@end...#import 
static char UIScrollViewPullToRefreshView;@implementation UIScrollView (GMPullToAction)@dynamic pullToRefreshView;...- (void)setPullToRefreshView:(GMPullToRefresh *)pullToRefreshView { [self willChangeValueForKey:@"pullToRefreshView"]; objc_setAssociatedObject(self, &UIScrollViewPullToRefreshView, pullToRefreshView, OBJC_ASSOCIATION_ASSIGN); [self didChangeValueForKey:@"pullToRefreshView"];} - (GMPullToRefresh *)pullToRefreshView { return objc_getAssociatedObject(self, &UIScrollViewPullToRefreshView);}...@end

 

(2)、然后继续看GMPullToRefresh类的实例化方法initWithScrollView:

- (id)initWithScrollView:(UIScrollView *)scrollView {    //初始化    self = [super initWithFrame:CGRectZero];    self.scrollView = scrollView;    self.frame = CGRectMake(0, -kFrameHeight, scrollView.bounds.size.width, kFrameHeight);    [_scrollView addSubview:self];    //定制提示文字    self.titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(kContent_Width/2.f-75/4.f, self.bounds.size.height*0.5-10, 75, 20)];    _titleLabel.font = [UIFont boldSystemFontOfSize:14];    _titleLabel.backgroundColor = [UIColor clearColor];    _titleLabel.textColor = kTextColor;    [self addSubview:_titleLabel];       //矩形上升动画图    ...       //圆圈转动动画    ...       //指定state    self.state = GMPullToRefreshStateHidden;       return self;}

  其中两个负责动画的类先不讨论,主要看self.state。它是一个枚举值,用来记录控件的状态,它的定义如下:

enum {    GMPullToRefreshStateHidden = 1,  //隐藏    GMPullToRefreshStateVisible,     //可见    GMPullToRefreshStateTriggered,   //已触发刷新    GMPullToRefreshStateLoading      //已在加载};typedef NSUInteger GMPullToRefreshState;

 

4、然后看第二个入口方法- (void)scrollViewDidScroll:(UIScrollView *)scrollView:

(1)、这个方法会在scrollView拖动的时候不断被调用,在这个方法中主要是执行了代码[scrollView didScroll]。- (void)didScroll方法也是定义在UIScrollView (GMPullToAction)类中,它的代码如下:

- (void)didScroll {    //这个self是指scrollView    CGPoint point=self.contentOffset;    if (self.pullToRefreshView) {        [self.pullToRefreshView scrollViewDidScroll:point];    }}

  在这个方法中,将scrollView实时的contentOffset传给了GMPullToRefresh的方法- (void)scrollViewDidScroll:(CGPoint)contentOffset;

 

(2)、GMPullToRefresh的- (void)scrollViewDidScroll:(CGPoint)contentOffset方法代码如下:

- (void)scrollViewDidScroll:(CGPoint)contentOffset {    if (self.state == GMPullToRefreshStateLoading) {        return;    }       //起点的y值(负数),它的绝对值也是pullToRefreshView的高度,也是触发刷新状态的高度    CGFloat scrollOffsetThreshold = self.frame.origin.y;       //已经触发刷新并且放开拖动了,就变成加载状态    if(self.state == GMPullToRefreshStateTriggered && !self.scrollView.isDragging){        self.state = GMPullToRefreshStateLoading;           //scrollView开始往下拖动(<0),并且未达到触发刷新的高度(>scrollOffsetThreshold),为可见状态    }else if(contentOffset.y < 0 &&  contentOffset.y > scrollOffsetThreshold && self.scrollView.isDragging && self.state != GMPullToRefreshStateLoading){        self.state = GMPullToRefreshStateVisible;           //scrollView往下拖动到已触发刷新的高度(
= 0 && self.state != GMPullToRefreshStateHidden){ self.state = GMPullToRefreshStateHidden; } //拖动过程中的动画效果 ...}

  这个方法根据scrollView实时的contentOffset来决定状态self.state,这会调用self.state的set方法。

 

(3)、GMPullToRefresh的setState:方法代码如下:

- (void)setState:(GMPullToRefreshState)newState {    _state = newState;    switch (newState) {        case GMPullToRefreshStateHidden:            ... //动画            [self setScrollViewContentInsetTop:0];                        break;                   case GMPullToRefreshStateVisible:            _titleLabel.text = NSLocalizedString(@"下拉刷新...",);            ...            break;                   case GMPullToRefreshStateTriggered:            _titleLabel.text = NSLocalizedString(@"松开刷新...",);            ...            break;                   case GMPullToRefreshStateLoading:            _titleLabel.text = NSLocalizedString(@"正在载入...",);            ...            [self setScrollViewContentInsetTop:self.frame.size.height];                       if(_actionHandler)                _actionHandler();            break;    }}

  可以看到,对于不同的state,会指定不同的文字(指定动画的语句省略了,会在下一篇讨论),而在加载状态,会调用第一个入口方法传进来的块代码,即是执行加载的语句。

 

(4)、在上面的方法中,在隐藏状态和加载状态还会调用一个方法setScrollViewContentInset:来指定scrollView的contentInset。当状态为隐藏的时候,将contentInset的top置为0;当状态为加载的时候,将contentInset的top置为pullToRefreshView的高度。并且将这个过程做成动画效果:

- (void)setScrollViewContentInsetTop:(CGFloat)top {    UIEdgeInsets inset = self.scrollView.contentInset;    inset.top = top;    [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionAllowUserInteraction|UIViewAnimationOptionBeginFromCurrentState animations:^{        self.scrollView.contentInset = inset;           } completion:^(BOOL finished) {    }];}

 

5、最后一个入口方法,是在当数据加载完毕的时候调用的,调用这个方法会把state置为隐藏,让加载画面动画恢复到下拉前状态:

- (void)stopAnimating {    self.state = GMPullToRefreshStateHidden;}

 

6、至此完成了下拉刷新控件的框架,下一篇博文会分析这个控件里的动画效果。

 

转载于:https://www.cnblogs.com/shayneyeorg/p/4713221.html

你可能感兴趣的文章
SQL(1)—增删改查
查看>>
Spring+Quartz 整合二:调度管理与定时任务分离
查看>>
ubuntu下编译ffmpeg并用eclipse调试
查看>>
oracle分页查询
查看>>
微信自定义分享
查看>>
CSS基础part1
查看>>
魔兽世界服务器Trinitycore分析二:auth server的main函数
查看>>
MFC防止进程重复建立
查看>>
3. Node.js REPL(交互式解释器)
查看>>
webview滑动事件 与内部html左右滑动事件冲突问题的解决办法
查看>>
傅里叶变换学习总结
查看>>
java面试题汇总(1)
查看>>
VC Dimension -衡量模型与样本的复杂度
查看>>
android 中 ViewPager 的平常用法 ViewPager+ Views
查看>>
POJ 2449 Remmarguts' Date (SPFA + A星算法) - from lanshui_Yang
查看>>
ZOJ 1654 二分匹配基础题
查看>>
【玩转Ubuntu】02. Ubuntu上搭建Android开发环境
查看>>
[蓝桥杯][2017年第八届真题]小计算器(模拟)
查看>>
小花梨的三角形(暴力上下扫三角形)
查看>>
Lua学习笔记之函数
查看>>