所闻所获4:下拉刷新控件2

  在上一篇博文讨论了下拉刷新控件的框架,这一篇博文将会主要讨论刷新过程中控件的动画效果。

1、首先回顾一下在GMPullToRefresh类中的初始化方法:

- (id)initWithScrollView:(UIScrollView *)scrollView {
//初始化
...

//定制提示文字
...

//矩形上升动画图
self.activityView=[self activityIndicatorView];

//圆圈转动动画
self.circleView=[[CircleProgessView alloc] initWithFrame:CGRectMake(self.titleLabel.frame.origin.x-30-5, self.bounds.size.height*0.5-15, 30, 30)];
[self addSubview:self.circleView];

//指定state
...

return self;
}

  其中分别有两个动画,一个是矩形上升动画,一个是圆形转圈动画。先来看看矩形上升动画,这个动画的基本原理是这样的:它放置了两张图片,蓝色图片在后、白色图片在前,动画的过程就是让白色图片的高度变小,让它从下往上地缩小,造成蓝色图片从下往上上升的效果。它的设置方法如下:

- (GMActivityView *)activityIndicatorView {
    if(!_activityView) {
        _activityView = [[GMActivityView alloc] initWithFrame:CGRectMake(self.titleLabel.frame.origin.x-30, self.bounds.size.height*0.5-10, 20, 20)];
        _activityView.hidesWhenStopped = NO;
        [self addSubview:_activityView];
    }
    return _activityView;
}

  可以看到,这是实例化了一个GMActivityView类的对象。

2、在GMActivityView类中的初始化方法如下:

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self configSubViews];
    }
    return self;
}

- (void)configSubViews {
    //用一张白色的图片先挡在蓝色图片上面
    self.frontView=[[UIView alloc] initWithFrame:self.bounds];
    UIImageView *fImage=[[UIImageView alloc] initWithFrame:self.frontView.bounds];;
    fImage.image=[UIImage imageNamed:FRONT_IMAGE];
    [self.frontView addSubview:fImage];
    self.frontView.backgroundColor=[UIColor clearColor];
    self.frontView.clipsToBounds=YES;

    //蓝色图片放后面
    self.backView=[[UIView alloc] initWithFrame:self.bounds];
    UIImageView *bImage=[[UIImageView alloc] initWithFrame:self.backView.bounds];;
    bImage.image=[UIImage imageNamed:BACK_IMAGE];
    [self.backView addSubview:bImage];
    self.backView.backgroundColor=[UIColor clearColor];
    self.backView.clipsToBounds=YES;

    [self addSubview:self.backView];
    [self addSubview:self.frontView];
}

3、那么就初始化好了这个页面,在上一篇博文中已经提到,在scrollView拖动的过程中,会不断调用到GMPullToRefresh类的scrollViewDidScroll:方法,并且将scrollView实时的contentOffset传进来。而在scrollViewDidScroll:方法中,会不断根据实时的contentOffset判断控件应处的state,如有需要就调用setState:方法修改控件的state。我们通过这两个方法来解析矩形的所有动画效果:

(1)、scrollViewDidScroll:方法,它除了根据实时的contentOffset修改state之外,还有一部分代码是用来根据contentOffset和state去定制不同的动画:

- (void)scrollViewDidScroll:(CGPoint)contentOffset {
    //根据实时的contentOffset设置state
    ...

    if (self.state==GMPullToRefreshStateHidden||self.state==GMPullToRefreshStateVisible) {
        //取绝对值
        float moveY = fabs(self.scrollView.contentOffset.y);
        if (moveY > kFrameHeight)
            moveY = kFrameHeight;
        //根据拖动距离和触发距离的比例,来定制圆弧的长度
        ...
        //根据拖动距离和触发距离的比例,来定制矩形的高度
        [self.activityView drawViewWithProgress:(moveY/kFrameHeight)];

    } else if (self.state==GMPullToRefreshStateTriggered) {
        ...
    }
}

  在上面的方法中,使用了drawViewWithProgress:方法,这个方法是根据拖动距离和触发距离的比例来定制矩形高度的。效果就是,在拖动到触发之前,矩形会随着拖动这个动作上升,它的代码如下:

- (void)drawViewWithProgress:(CGFloat)progress{
    if (progress>1) {
        progress=1;
    }
    if (progress<0) {
        progress=0;
    }

    CGRect frontViewFrame = self.frontView.frame;
    CGFloat frontViewHeight = self.frame.size.height*(1-progress);
    [self.frontView setFrame:CGRectMake(frontViewFrame.origin.x, frontViewFrame.origin.y, frontViewFrame.size.width, frontViewHeight)];
}

(2)、setState:方法,这个方法在设定state之后,就会根据不同的state去定制动画了。这个方法中关于动画的代码如下:

- (void)setState:(GMPullToRefreshState)newState {
    _state = newState;
    switch (newState) {
        case GMPullToRefreshStateHidden:
            ...
            [self.activityView stopAnimation];
            self.activityView.isFull = NO;
            ...
            break;

        case GMPullToRefreshStateVisible:
            ...
            [self.activityView stopAnimation];
            self.activityView.isFull = NO;
            break;

        case GMPullToRefreshStateTriggered:
            ...
            self.activityView.isFull = YES;
            break;

        case GMPullToRefreshStateLoading:
            ...
            [self.activityView startAnimation];
            ...
            break;
    }
}

  其中总共调用到了GMActivityView 类的3个方法:startAnimation、isFull和stopAnimation。这3个方法就是矩形图形的所有动画效果了,它们的代码如下:

- (void)startAnimation {
    __weak GMActivityView *weakSelf = self;
    self.hidden = NO;
    self.isFull = NO;
    self.isStop = NO;
    CGRect rect = self.bounds;
    rect.size.height = 0;

    [UIView animateWithDuration:SPEED_TIME delay:SPEED_DELAY options:UIViewAnimationOptionCurveLinear animations:^{
        //frontView白色图片从完全显示变化到高度为0,出现了蓝色图片高度增加的效果
        weakSelf.frontView.frame = rect;
    } completion:^(BOOL finished) {
        if (weakSelf.isStop) {
            return;
        }
        //不断循环
        [weakSelf startAnimation];
    }];
}

//这个方法指定的是白色图片的高度:当!isFull的时候,白色图片完全显示,蓝色图片看不见;当isFull的时候,白色图片不见,蓝色图片完全显示。
- (void)setIsFull:(BOOL)isFull {
    _isFull = isFull;
    if (!isFull) {
        self.frontView.frame = self.bounds;
    }else {

        CGRect rect = self.bounds;
        rect.size.height = 0;
        self.frontView.frame = rect;
    }
}

- (void)stopAnimation {
    self.isStop = YES;
    self.isFull = YES;
    if (self.hidesWhenStopped) {
        self.hidden = YES;
    }
}

  这就是矩形图形的整个动画过程。

4、接下来看看转圈圆形的动画,首先回到1中的GMPullToRefresh类的初始化方法:

- (id)initWithScrollView:(UIScrollView *)scrollView {
//初始化
...

//定制提示文字
...

//矩形上升动画图
...

//圆圈转动动画
self.circleView=[[CircleProgessView alloc] initWithFrame:CGRectMake(self.titleLabel.frame.origin.x-30-5, self.bounds.size.height*0.5-15, 30, 30)];
[self addSubview:self.circleView];

//指定state
...

return self;
}

5、那么下一步就看看CircleProgessView类的初始化方法:

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = [UIColor clearColor];
        progress = 0;
    }
    return self;
}

6、这样就把圆弧初始化好了,并指定了它圆弧的完整度为0。它的动画效果和矩形上升图类似,同样是在scrollViewDidScroll:方法中,不断根据实时的contentOffset判断控件应处的state,如有需要就调用setState:方法修改控件的state。同样地,我们通过这两个方法来解析矩形的所有动画效果:

(1)、scrollViewDidScroll:方法,这个方法中定制动画的代码中,关于圆弧的代码不只是转动的。当下拉距离未达到触发距离的过程中,圆弧的动画是从短变长的,在达到触发距离的时候圆弧刚好画完。这个时候如果继续往下拖动,圆弧会开始转动,根据拖动的速度和距离决定圆弧转动的速度和角度。如果放开手,圆弧进入自主转动的动画,这个效果定义在setState:方法里:

- (void)scrollViewDidScroll:(CGPoint)contentOffset {
    ...

    if (self.state==GMPullToRefreshStateHidden||self.state==GMPullToRefreshStateVisible) {
        //取绝对值
        float moveY = fabs(self.scrollView.contentOffset.y);
        if (moveY > kFrameHeight)
            moveY = kFrameHeight;
        //根据拖动距离和触发距离的比例,来定制圆弧的长度
        [self.circleView drawCircleWithProgress:(moveY-kFrameHeight*0.5) / (kFrameHeight*0.5)];
        //用同样的方法来定制方形的高度
        ...

    } else if (self.state==GMPullToRefreshStateTriggered) {
        [self.circleView drawCircleWithProgress:1.];
        //超过触发距离了,圆圈继续转
        float moveY=fabs(self.scrollView.contentOffset.y);
        self.circleView.transform = CGAffineTransformMakeRotation((moveY-kFrameHeight)/60*360*M_PI/180);
    }
}

  可以看到,它引用了CircleProgessView类的drawCircleWithProgress:方法,方法内容如下:

-(void)drawCircleWithProgress:(CGFloat)mProgress{
    progress = mProgress;
    [self setNeedsDisplay];//这个方法会调用drawRect:方法
}

- (void)drawRect:(CGRect)rect {
    if (progress>1) {
        progress=1;
    }
    if (progress<0) {
        progress=0;
    }
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetLineWidth(context, 1);
    CGContextSetLineCap(context, kCGLineCapRound);
    CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
    CGFloat startAngle = 0;
    CGFloat step = 11.4*M_PI/6 * progress;//弧线对应的角度
    CGContextAddArc(context, self.bounds.size.width/2, self.bounds.size.height/2, self.bounds.size.width/2-1, startAngle, startAngle+step, 0);
    //CGContextAddArc(CGContextRef c, CGFloat x, CGFloat y, CGFloat radius, CGFloat startAngle, CGFloat endAngle, int clockwise)
    //x和y是原点位置
    //radius是半径
    //startAngle为0,即是以坐标原点为圆心时x轴的方向
    //endAngle为45*(M_PI/180)是往下方偏移45度
    //clockwise为0是顺时针画,为1是逆时针画

    CGContextStrokePath(context);
}

(2)、然后再来看看setState:方法,它会根据不同的state定制不同的动画:

- (void)setState:(GMPullToRefreshState)newState {
    _state = newState;
    switch (newState) {
        case GMPullToRefreshStateHidden:
            [self.circleView stopAnimation];
            ...
            break;

        case GMPullToRefreshStateVisible:
            ...
            [self.circleView stopAnimation];
            ...
            break;

        case GMPullToRefreshStateTriggered:
            ...
            self.circleView.transform = CGAffineTransformMakeRotation(0);
            ...
            break;

        case GMPullToRefreshStateLoading:
            ...
            self.circleView.transform = CGAffineTransformMakeRotation(0);
            [self.circleView startAnimation];
            ...
            break;
    }
}

  主要调用到了startAnimation方法和stopAnimation方法,它们的代码如下:

-(void)startAnimation {
    [self drawCircleWithProgress:1];

    //绕z轴旋转
    CABasicAnimation* rotate =  [CABasicAnimation animationWithKeyPath: @"transform.rotation.z"];
    rotate.removedOnCompletion = FALSE;
    rotate.fillMode = kCAFillModeForwards;
    [rotate setToValue: [NSNumber numberWithFloat: M_PI / 2]];
    rotate.repeatCount = HUGE_VALF;

    rotate.duration = 0.25;
    rotate.cumulative = TRUE;

    //控制动画速度
    rotate.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    //kCAMediaTimingFunctionLinear 线性(匀速)|
    //kCAMediaTimingFunctionEaseIn 先慢|
    //kCAMediaTimingFunctionEaseOut 后慢|
    //kCAMediaTimingFunctionEaseInEaseOut 先慢 后慢 中间快|
    //kCAMediaTimingFunctionDefault 默认|

    [self.layer addAnimation:rotate forKey:@"rotateAnimation"];
}

-(void)stopAnimation{
    [self.layer removeAnimationForKey:@"rotateAnimation"];
}

  至此完成了圆弧转圈的动画。

时间: 2024-10-06 12:47:30

所闻所获4:下拉刷新控件2的相关文章

所闻所获3:下拉刷新控件1

本文主要是讨论在最近项目中遇到的一个下拉刷新控件,这个控件的效果如下图: 在这里会用两篇博文的篇幅来解析这个控件,第一篇解析控件的框架,第二篇解析动画.源代码可以在下面的链接下载: TestPullToRefresh.zip 1.这个控件由以下几个文件组成:GMPullToAction.CircleProgressView.GMActivityView,其中GMPullToAction文件包含两个类:GMPullToRefresh和UIScrollView (GMPullToAction),Ci

自定义下拉刷新控件

一.功能效果 1.在很多app中,在信息展示页面,当我们向下拖拽时,页面会加载最新的数据,并有一个短暂的提示控件出现,有些会有加载进度条,有些会记录加载日期.条目,有些还带有加载动画.其基本实现原理都相仿,本文中将探讨其实现原理,并封装出一个简单的下拉刷新控件 2.自定义刷新工具简单的示例 二.系统提供的下拉刷新工具 1.iOS6.0以后系统提供了自己的下拉刷新的控件:UIRefreshControl .例如,refreshControl,作为UITableViewController中的一个属

[Android]下拉刷新控件RefreshableView的实现

需求:自定义一个ViewGroup,实现可以下拉刷新的功能.下拉一定距离后(下拉时显示的界面可以自定义任何复杂的界面)释放手指可以回调刷新的功能,用户处理完刷新的内容后,可以调用方法onCompleteRefresh()通知刷新完毕,然后回归正常状态.效果如下:     源代码:RefreshableView(https://github.com/wangjiegulu/RefreshableView) 分析: 我们的目的是不管什么控件,只要在xml中外面包一层标签,那这个标签下面的所有子标签所

Android 解决下拉刷新控件和ScrollVIew的滑动冲突问题。

最近项目要实现ScrollView中嵌套广告轮播图+RecyleView卡片布局,并且RecyleView按照header和内容的排列样式,因为RecyleView的可扩展性很强,所以我毫无疑问的选择了它,而且让RecyleView实现了可拖拽的效果, 最后我再加上了下拉刷新的效果(这里我用的下拉刷新控件是三方的SmartRefreshLayout).记得刚开始实现这个效果的时候还是十分的得心印手.可是当我测试的时候,发现RecyleView的子item的拖拽效果并不流畅,起初我以 为是由于Re

iOS:下拉刷新控件UIRefreshControl的详解

下拉刷新控件:UIRefreshControl 1.具体类信息: @interface UIRefreshControl : UIControl //继承控制类 - (instancetype)init; @property (nonatomic, readonly, getter=isRefreshing) BOOL refreshing; //是否可以刷新 @property (nonatomic, retain) UIColor *tintColor; //控件颜色 @property (

Android下拉刷新控件--PullToRefresh的简单使用

Android中很多时候都会用到上下拉刷新,这是一个很常用的功能,Android的v4包中也为我们提供了一种原生的下拉刷新控件--SwipeRefreshLayout,可以用它实现一个简洁的刷新效果,但今天我们的主角并不是它,而是一个很火的第三方的上下拉刷新控件--PullToRefresh.PullToRefresh包括PullToRefreshScrollView.PullToRefreshListView.PullToRefreshGridView等等很多为我们提供的控件,我们可以在xml

Android下拉刷新控件SwipeRefreshLayout源码浅析

SwipeRefreshLayout是Android官方的下拉刷新控件,使用简单,界面美观,不熟悉的朋友可以随便搜索了解一下,这里就不废话了,直接进入正题. 这种下拉刷新控件的原理不难,基本就是监听手指的运动,获取手指的坐标,通过计算判断出是哪种操作,然后就是回调相应的接口了.SwipeRefreshLayout是继承自ViewGroup的,根据Android的事件分发机制,触摸事件应该是先传递到ViewGroup,根据onInterceptTouchEvent的返回值决定是否拦截事件的,那么就

Android——谷歌官方下拉刷新控件SwipeRefreshLayout(转)

转自:http://blog.csdn.net/zouzhigang96/article/details/50476402 版权声明:本文为博主原创文章,未经博主允许不得转载. 前言: 如今谷歌推出了更官方的下拉刷新控件, 这无疑是对安卓开发人员来说是个好消息,很方便的使用这个SwipeRefreshLayout控件实现下拉刷新功能.Android4.0以下的版本需要用到 Android-support-v4.jar包才能用到 android-support-v4.jar 包下载地址:http:

Android仿苹果版QQ下拉刷新实现(一) ——打造简单平滑的通用下拉刷新控件

前言: 因为公司人员变动原因,导致了博主四个月没有动安卓,一直在做IOS开发,如今接近年前,终于可以花一定的时间放在安卓上了.好了,废话不多说,今天我们要带来的效果是苹果版本的QQ下拉刷新.首先看一下目标效果以及demo效果:      因为此效果实现的步骤较多,所以今天博主要实现以上效果的第一步——打造一个通用的下拉刷新控件,具体效果如下: GIF图片比较大,还希望读者能耐心等待一下下从效果图中可以看出,我们的下拉刷新的滑动还是很流畅的,可能大多数开发者用的是XListview或者PullTo