动画特效九:下拉刷新

这一节为大家介绍一个经常使用的操作:下拉刷新。我这里的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];
}
时间: 2024-10-10 02:09:56

动画特效九:下拉刷新的相关文章

快速下拉刷新动画

-(void)setupTableview{ //添加下拉的动画图片 //设置下拉刷新回调 [self.tableView addGifHeaderWithRefreshingTarget:self refreshingAction:@selector(loadNewData)]; //设置普通状态的动画图片 NSMutableArray *idleImages = [NSMutableArray array]; for (NSUInteger i = 1; i<=60; ++i) { // U

ListView下拉刷新

public class MyListView extends ListView implements OnScrollListener { private static final int STATE_NORMAL = 0;  // 正常状态 private static final int STATE_PULL = 1;    // 下来状态 private static final int STATE_RELEASE = 2; // 释放状态 private static final in

H5下拉刷新特效demo,动画流畅

<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-wi

Xamarin.Form 下拉刷新动画

好像园子里对 Xamarin 感兴趣的人很少啊... 来, 先给各位爷们逗个笑, 本山大爷本色出演: 照例, 上源码: https://github.com/gruan01/ListViewExtend 目前只有 WP 的效果, Android 还在研究, IOS 的还没计划. ------------------------------------------------------ Xamarin.Form 的 ListView 只支持下拉刷新 (这里有用法), 上拉 加载更多 没有对应的事

Android UI- PullToRrefresh自定义下拉刷新动画

Android UI- PullToRrefresh自定义下拉刷新动画 如果觉得本文不错,麻烦投一票,2014年博客之星投票地址:http://vote.blog.csdn.net/blogstar2014/details?username=wwj_748#content 本篇博文要给大家分享的是如何使用修改开源项目PullToRrefresh下拉刷新的动画,来满足我们开发当中特定的需求,我们比较常见的一种下拉刷新样式可能是以下这种: 就是下拉列表的时候两个箭头上下翻转,更改日期文本和刷新状态,

自定义(下拉刷新、上拉加载)帧动画

前些天搞了个系统的下拉刷新跟上拉加载,由于效果一般所以才会有了今天这篇博文 对于大多数的码农来说,能弄出些自己感兴趣的好东西还是比较开心的.-- package com.example.administrator.xlistview; import android.content.Context; import android.graphics.drawable.AnimationDrawable; import android.util.AttributeSet; import android

Android源码解析--超好看的下拉刷新动画

本篇博客代码下载地址:https://github.com/Yalantis/Taurus 最近在github上看到了好多高端.大气.上档次的动画效果,如果给你的项目中加上这些动画,相信你的app一定很优秀,今天给大家分析一下来自Yalantis的一个超好看的下拉刷新动画. 首先我们看一下效果如何: 怎么样?是不是很高大上?接下来我们看一下代码: 一.首先我们需要自定义刷新的动态RefreshView(也就是下拉时候的头) 1.初始化头所占用的Dimens private void initia

下拉刷新动画研究

昨天公司提了一个需求.说是要有自己特色的下拉刷新上拉载入很多其它,随后就去万能的github上面看看自己之前存过的那些.偶然间看到这个Android-AnimatePullToRefreshListView,这玩意能够使用gif图片或者帧动画去运行headview动画载入,而且是基于chrisbanes的Android-PullToRefresh,这样还方便与我项目无缝衔接 上一张效果图 大体上简单说明下: 1.gif与帧动画播放採用的是android-gif-drawable 2.主题上是改动

使用MJRefresh自定义下拉刷新,上拉加载动画

有时候我们需要自己设置下拉刷新,上拉加载动画的实现,这里主要是记录下使用MJRefresh自定义下拉刷新,上拉加载动画..... 下拉刷新我们只需要继承MJRefreshGifHeader即可: 实现代码如下: - (void)prepare{ [super prepare]; self.stateLabel.hidden = NO; self.lastUpdatedTimeLabel.hidden = YES; [self setImages:@[[UIImage imageNamed:@"v