zoukankan      html  css  js  c++  java
  • 动画特效九:下拉刷新

    这一节为大家介绍一个经常使用的操作:下拉刷新。我这里的Demo。是模拟情况。没有涉及到详细的数据源。

    真实的下拉刷新控件还是比較复杂的。

    先看看效果图。



    关于UITableView的代码就忽略不计了。那不是本节解说的重点。

    动画分析:

    1. UITableView上方有一个View,就是用来展示下拉刷新的特效的。所以全然能够自己定义一个View来封装它,我将其命名为RefreshView。

    2. 自己定义的View上面有两个图层动画(1. 转圈圈。2. 飞机旋转), 所以能够自己定义两个图层:ovalShapeLayer和airplaneLayer。

    3. 因为自己定义的View中,要推断scrollView的拖拽等功能,所以要将UITableView传递过来。而且自己实现UIScrollViewDelegate协议。


    代码分析:

    1. RefreshView.h ,初始化RefreshView的frame及将UITableView传递过来。

    @interface RefreshView : UIView
    - (instancetype)initWithFrame:(CGRect)frame scrollView:(UIScrollView *)scrollView;
    @end

    2. RefreshView.m 属性定义部分

    @interface RefreshView()<UIScrollViewDelegate>
    /*主界面传递过来的scrollView*/
    @property (nonatomic, strong) UIScrollView *scrollView;
    /*转圈圈的shapeLayer*/
    @property (nonatomic, strong) CAShapeLayer *ovalShapeLayer;
    /*飞机的layer*/
    @property (nonatomic, strong) CALayer *airplaneLayer;
    @end

    3. initWithFrame:scrollView的实现代码

    - (instancetype)initWithFrame:(CGRect)frame scrollView:(UIScrollView *)scrollView {
        if (self = [super initWithFrame:frame]) {
            self.scrollView = scrollView;
            self.scrollView.delegate = self;
            
            [self addBgImage];
            
        }
        return self;
    }
    
    - (void)addBgImage {
        UIImageView *bgImageView = [[UIImageView alloc] init];
        bgImageView.image = [UIImage imageNamed:@"refresh-view-bg.png"];
        bgImageView.frame = self.bounds;
        bgImageView.contentMode = UIViewContentModeScaleAspectFill;
        bgImageView.clipsToBounds = YES;
        [self addSubview:bgImageView];
    }

    然后在ViewController中初始化RefreshView。

    - (void)viewDidLoad {
        [super viewDidLoad];
        CGRect refreshViewFrame = CGRectMake(0, -kRefreshViewHeight, self.view.frame.size.width, kRefreshViewHeight);
        self.refreshView = [[RefreshView alloc] initWithFrame:refreshViewFrame scrollView:self.tableView];
        [self.tableView addSubview:self.refreshView];
        
        self.tableView.contentOffset = CGPointMake(0, kRefreshViewHeight);
    }

    注意到 kRefreshViewHeight是一个宏,其就是RefreshView的实际高度

    #define kRefreshViewHeight 110.0
    并且它的frame的y值是负数。所以默认情况下,它是看不见。当你将UITableView往下拉的时候,便能够看见。

    至此,效果图例如以下:



    4. 加入圆环转圈圈动画。

    在initWithFrame:scrollView方法的底部加入例如以下代码:

    - (void)addOvalShapeLayer {
        self.ovalShapeLayer = [CAShapeLayer layer];
        self.ovalShapeLayer.strokeColor = [UIColor whiteColor].CGColor;
        self.ovalShapeLayer.fillColor = [UIColor clearColor].CGColor;
        self.ovalShapeLayer.lineWidth = 4.0;
        self.ovalShapeLayer.lineDashPattern = @[@(2),@(3)];
        CGFloat refreshRadius = self.frame.size.height / 2 * 0.8;
        self.ovalShapeLayer.path = [UIBezierPath bezierPathWithOvalInRect:
                                    CGRectMake(self.frame.size.width / 2 - refreshRadius, self.frame.size.height / 2 - refreshRadius, 2 * refreshRadius, 2 *refreshRadius)].CGPath;
        [self.layer addSublayer:self.ovalShapeLayer];
    }
    对lineDashPattern进行说明。注意到,它是一个数组而且有两个值。第一个值表示每段圆圈的宽度。第二个值表示段与段之间的距离。我们看看效果图:



    在initWithFrame:scrollView方法的底部加入例如以下代码:

    - (void)addAirplaneLayer {
        self.airplaneLayer = [CALayer layer];
        self.airplaneLayer.opacity = 0.0;
        UIImage *image = [UIImage imageNamed:@"airplane.png"];
        self.airplaneLayer.contents = (__bridge id)(image.CGImage);
        self.airplaneLayer.frame = CGRectMake(0, 0, image.size.width, image.size.height);
        self.airplaneLayer.position = CGPointMake(self.frame.size.width / 2 + self.frame.size.height / 2 * 0.8, self.frame.size.height / 2);
        [self.layer addSublayer:self.airplaneLayer];
    }

    这样飞机就出如今圆圈的右側了。紧接着,我们为圆环及飞机加入动画效果。

    先看看圆圈的动画效果。注意到。当你将scrollView网下拉的时候,圆圈的白色就越来越多;当往上拉的时候,圆圈的白色就越来越少。

    而这个效果能够通过ovalshapeLayer的strokeEnd属性值来控制。strokeEnd的取值范围是(0.0~1.0),当为0.0的时候。看不到圆圈;当为1.0的时候,整个圆圈显示完整;当为0.25和0.50的时候。效果图分别例如以下:


    所以,我们在代码方法中写入下面代码,便能够看出圆圈的即时变化

    // 拖动tableview的时候。就显示圆圈添加或者降低效果(在scrollView拖拽过程中会一直触发)
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    
            // scrollView.contentOffset.y 往下拉值为负,往上提值为正
            CGFloat offsetY = MAX(-(scrollView.contentOffset.y + scrollView.contentInset.top), 0);
            self.progress = MIN(MAX(offsetY / self.frame.size.height, 0), 1);
            [self redrawFromProgress:self.progress];
    
    }
    - (void)redrawFromProgress:(CGFloat)progress {
        self.ovalShapeLayer.strokeEnd = progress;
        // 伴随着滚动。有种透明的效果
        self.airplaneLayer.opacity = progress;
    }

    注意到progress属性。它就是用来保存用户将RefreshView拖动到的位置,它的值的范围(0.0~1.0)

    /*拉动的距离(0~1)*/
    @property (nonatomic, assign) CGFloat progress;
    因为有动画的開始和结束相应的操作,所以我定义一个bool变量。用来推断相应的状态

    /*是否正在刷新*/
    @property (nonatomic, assign, getter=isRefreshing) BOOL refreshing;

    5. 当用户拉动scrollView而且拖拽的范围使得RefreshView全然可见,也就是self.progress的值为1的时候,应该运行刷新操作。实际情况下。这个时候应该进行完毕网络数据的载入,我这里用延时4秒,仿真载入数据。

    代码例如以下:

    // 在scrollView停止拖拽的时候会触发
    - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
        if (!self.isRefreshing && self.progress >= 1.0) {
            [self beginRefreshing];
        }
    }

    - (void)beginRefreshing {
    
        self.refreshing = YES;
        [UIView animateWithDuration:4.0 animations:^{
            UIEdgeInsets newInsets = self.scrollView.contentInset;
            newInsets.top += self.frame.size.height;
            self.scrollView.contentInset = newInsets;
        } completion:^(BOOL finished) {
            [self endRefreshing];
        }];
        
        // 圆圈效果
        CABasicAnimation *strokeStartAnimation = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
        strokeStartAnimation.fromValue = @(-0.5);
        strokeStartAnimation.toValue = @(1.0);
        
        CABasicAnimation *strokeEndAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
        strokeEndAnimation.fromValue = @(0.0);
        strokeEndAnimation.toValue = @(1.0);
    
        CAAnimationGroup *strokeAnimationGroup = [CAAnimationGroup animation];
        strokeAnimationGroup.duration = 1.5;
        strokeAnimationGroup.repeatDuration = 6.0;
        strokeAnimationGroup.animations = @[strokeStartAnimation, strokeEndAnimation];
        [self.ovalShapeLayer addAnimation:strokeAnimationGroup forKey:nil];
        
        // 飞机效果
        // 1. 绕着圆圈移动
        CAKeyframeAnimation *flightAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
        flightAnimation.path = self.ovalShapeLayer.path;
        flightAnimation.calculationMode = kCAAnimationPaced;
        
        // 2. 有转动效果
        CABasicAnimation *airplaneOrientationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
        airplaneOrientationAnimation.fromValue = @0;
        airplaneOrientationAnimation.toValue = @(2 * M_PI);
        
        CAAnimationGroup *flightAnimationGroup = [CAAnimationGroup animation];
        flightAnimationGroup.duration = 1.5;
        flightAnimationGroup.repeatDuration = 6.0;
        flightAnimationGroup.animations = @[flightAnimation,airplaneOrientationAnimation];
        [self.airplaneLayer addAnimation:flightAnimationGroup forKey:nil];
    }
    
    - (void)endRefreshing {
        self.refreshing = NO;
        [UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
            UIEdgeInsets newInsets = self.scrollView.contentInset;
            newInsets.top -= self.frame.size.height;
            self.scrollView.contentInset = newInsets;
        } completion:nil];
    }

  • 相关阅读:
    PE格式详细讲解10 系统篇10|解密系列
    复杂的数据类型1 C++快速入门07
    复杂的数据类型2 C++快速入门08
    复杂的数据类型2 C++快速入门08
    复杂的数据类型1 C++快速入门07
    PE格式详细讲解10 系统篇10|解密系列
    Win32基础知识1 Win32汇编语言002
    开题篇 Win32汇编语言001
    开题篇 Win32汇编语言001
    Win32基础知识1 Win32汇编语言002
  • 原文地址:https://www.cnblogs.com/yangykaifa/p/6768271.html
Copyright © 2011-2022 走看看