zoukankan      html  css  js  c++  java
  • 所闻所获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"];
    }

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

  • 相关阅读:
    android 关于双卡设置
    android apk反编译和odex转dex
    Android中获取系统内存信息以及进程信息ActivityManager的使用(一)
    ubuntu banshee music
    linux 查看文件夹大小
    嵌入式Linux学习笔记之GPIO接口
    Android中MediaButtonReceiver广播监听器的机制分析
    在Ubuntu中VirtualBox下xp使用usb设备
    beyond compare 与git diff整合
    Linux学习笔记一 Linux基础知识认知以及初识Linux下C编程入门
  • 原文地址:https://www.cnblogs.com/shayneyeorg/p/4713357.html
Copyright © 2011-2022 走看看