最近在网上看到一个很酷的下拉刷新效果(http://iostuts.io/2015/10/17/elastic-bounce-using-uibezierpath-and-pan-gesture/)。自己试着实现了一下其中的果冻回弹效果。

效果


DEMO


逻辑


  • 下图p1,蓝色部分图形是一个CAShapeLayer,他的形状由UIBezierPath的路径组成的。

  • 这个路径是由r1,r2,r3,r4,r5这5个红点确定的。其中r1,r2,r3,r4都是不动点,唯一可以动的是r5点

  • 根据上面的动态图可以看出,CAShapeLayer的形状是随着r5红点的移动而相应变化的,所以只要获得r5的坐标变化就可以用UIBezierPath做出相应的路径,然后就可以形成相应的形状。

实现


  • 初始化CAShapeLayer
1
2
3
4
5
6
- (void)configShapeLayer
    _shapeLayer = [CAShapeLayer layer];
    _shapeLayer.fillColor = [UIColor colorWithRed:57/255.0 green:67/255.0 blue:89/255.0 alpha:1.0].CGColor;
    [self.layer addSublayer:_shapeLayer];
}
  • 初始化r5点
1
2
3
4
5
6
7
8
9
- (void)configCurveView
{
    // _curveView就是r5点
    _curveX = SYS_DEVICE_WIDTH/2.0;       // r5点x坐标
    _curveY = MIN_HEIGHT;                 // r5点y坐标
    _curveView = [[UIView alloc] initWithFrame:CGRectMake(_curveX, _curveY, 3, 3)];
    _curveView.backgroundColor = [UIColor redColor];
    [self addSubview:_curveView];
}
  • 添加移动手势&CADisplayLink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (void)configAction
{
    _mHeight = 100;                       // 手势移动时相对高度
    _isAnimating = NO;                    // 是否处于动效状态
 
    // 手势
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanAction:)];
    self.userInteractionEnabled = YES;
    [self addGestureRecognizer:pan];
 
    // calculatePath方法是算出在运行期间_curveView的坐标,从而确定_shapeLayer的形状
    _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(calculatePath)];
    [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    // 在手势结束的时候才调用calculatePath方法,所以一开始是暂停的
    _displayLink.paused = YES;    
}
  • 手势解析
    • 手势移动时,r5红点跟着手势移动,_shapeLayer则根据r5的坐标来扩大自己的区域

    • 手势结束时,r5红点通过UIView的动画方法来改变r5的坐标,同时_shapeLayer根据r5的坐标缩小自己的区域并最终返回原形。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
- (void)handlePanAction:(UIPanGestureRecognizer *)pan
{
    if(!_isAnimating)
    {
        if(pan.state == UIGestureRecognizerStateChanged)
        {
            // 手势移动时,_shapeLayer跟着手势向下扩大区域
            CGPoint point = [pan translationInView:self];
 
            // 这部分代码使r5红点跟着手势走
            _mHeight = point.y*0.7 + MIN_HEIGHT;
            _curveX = SYS_DEVICE_WIDTH/2.0 + point.x;
            _curveY = _mHeight > MIN_HEIGHT ? _mHeight : MIN_HEIGHT;
            _curveView.frame = CGRectMake(_curveX,
                                          _curveY,
                                          _curveView.frame.size.width,
                                          _curveView.frame.size.height);
 
            // 根据r5坐标,更新_shapeLayer形状
            [self updateShapeLayerPath];
 
        }
        else if (pan.state == UIGestureRecognizerStateCancelled ||
                 pan.state == UIGestureRecognizerStateEnded ||
                 pan.state == UIGestureRecognizerStateFailed)
        {
            // 手势结束时,_shapeLayer返回原状并产生弹簧动效
            _isAnimating = YES;
            _displayLink.paused = NO;           //开启displaylink,会执行方法calculatePath.
 
            // 弹簧动效
            [UIView animateWithDuration:1.0
                                  delay:0.0
                 usingSpringWithDamping:0.5
                  initialSpringVelocity:0
                                options:UIViewAnimationOptionCurveEaseInOut
                             animations:^{
 
                // 曲线点(r5点)是一个view.所以在block中有弹簧效果.然后根据他的动效路径,在calculatePath中计算弹性图形的形状
                _curveView.frame = CGRectMake(SYS_DEVICE_WIDTH/2.0, MIN_HEIGHT, 3, 3);
 
            } completion:^(BOOL finished) {
 
                if(finished)
                {
                    _displayLink.paused = YES;
                    _isAnimating = NO;
                }
 
            }];
        }
    }
}
  • 根据r5的位置,更新_shapeLayer形状
1
2
3
4
5
6
7
8
9
10
11
12
- (void)updateShapeLayerPath
{
    // 更新_shapeLayer形状
    UIBezierPath *tPath = [UIBezierPath bezierPath];
    [tPath moveToPoint:CGPointMake(0, 0)];  //r1点
    [tPath addLineToPoint:CGPointMake(SYS_DEVICE_WIDTH, 0)];// r2点
    [tPath addLineToPoint:CGPointMake(SYS_DEVICE_WIDTH,  MIN_HEIGHT)]; //r4点
    [tPath addQuadCurveToPoint:CGPointMake(0, MIN_HEIGHT)
                  controlPoint:CGPointMake(_curveX, _curveY)]; // r3,r4,r5确定的一个弧线
    [tPath closePath];
    _shapeLayer.path = tPath.CGPath;
}
  • 计算弹簧效果坐标
1
2
3
4
5
6
7
8
- (void)calculatePath
{
    // 由于手势结束时,r5执行了一个UIView的弹簧动画,把这个过程的坐标记录下来,并相应的画出_shapeLayer形状
    CALayer *layer = _curveView.layer.presentationLayer;
    _curveX = layer.position.x;
    _curveY = layer.position.y;
    [self updateShapeLayerPath];
}


  • r5点的作用非常重要,因为直接对CAShapeLayer实现动效不太好实现。所以通过对r5点实现弹簧动效,记录r5点的坐标,再用UIBezierPath形成路径,最后赋予CAShapeLayer,间接的完成了CAShapeLayer的弹簧动效。

链接:

iOS - 用 UIBezierPath 实现果冻效果

Elastic view animation using UIBezierPath

iOS - 用 UIBezierPath 实现果冻效果的更多相关文章

  1. CADisplayLink+弹簧动画实现果冻效果

    项目中在Tabbar中间的按钮要从底部弹出视图并有果冻效果,在CocoaChina中找了一篇博客用 UIBezierPath 实现果冻效果,github,自己就按着上面的demo修改了一下( 之前也是 ...

  2. iOS开发——图形编程OC篇&粘性动画以及果冻效果

    粘性动画以及果冻效果 在最近做个一个自定义PageControl——KYAnimatedPageControl中,我实现了CALayer的形变动画以及CALayer的弹性动画,效果先过目: 先做个提纲 ...

  3. 谈谈iOS中粘性动画以及果冻效果的实现

    在最近做个一个自定义PageControl——KYAnimatedPageControl中,我实现了CALayer的形变动画以及CALayer的弹性动画,效果先过目: https://github.c ...

  4. 转:谈谈iOS中粘性动画以及果冻效果的实现

    在最近做个一个自定义PageControl——KYAnimatedPageControl中,我实现了CALayer的形变动画以及CALayer的弹性动画,效果先过目: 先做个提纲: 第一个分享的主题是 ...

  5. iOS 之UIBezierPath

    代码地址如下:http://www.demodashi.com/demo/11602.html 在之前的文章中,由于用到过UIBezierPath这个类,所以这里就对这个类进行简单的记录一下,方便自己 ...

  6. XE8 for iOS 状态栏的几种效果

    XE8 实现 iOS 状态栏的几种效果: 一.状态栏底色: 开一个新工程. 设定 Fill.Color 颜色属性. 设定 Fill.Kind = Solid. 无需修改任何官方源码. 二.隐藏状态栏( ...

  7. ios点击产生波纹效果

    ios点击产生波纹效果 by 伍雪颖 - (void)viewDidLoad { [super viewDidLoad]; RippleView = [[UIView alloc] initWithF ...

  8. iOS CAReplicatorLayer 实现脉冲动画效果

    iOS CAReplicatorLayer 实现脉冲动画效果 效果图 脉冲数量.速度.半径.透明度.渐变颜色.方向等都可以设置.可以用于地图标注(Annotation).按钮长按动画效果(例如录音按钮 ...

  9. xmarin live player 连接 IOS以及安卓实现实时效果查看

    原文:xmarin live player 连接 IOS以及安卓实现实时效果查看 在之前有介绍过xamarin 单独IOS项目开发的运行环境搭建,但是这段时间我看到了xmarin forms 3.0  ...

随机推荐

  1. Warning: Attempt to present on whose view is not in the window hierarchy!

    当我想从一个VC跳转到另一个VC的时候,一般会用 - (void)presentViewController:(UIViewController *)viewControllerToPresent a ...

  2. 学习Coding-iOS开源项目日志(三)

    继续前两篇,接着本第三篇<学习Coding-iOS开源项目日志(三)>讲解Coding-iOS开源项目. 前 言:作为初级程序员,想要提高自己的水平,其中一个有效的学习方法就是学习别人好的 ...

  3. gitlab+gerrit+jenkins持续集成框架

    1.持续集成之gitlab+gerrit+jenkins 1.1. GitLab 1.1.1. 简介 GitLab 是一个使用使用Ruby on Rails搭建的,用于仓库管理系统的开源项目.使用Gi ...

  4. Java中怎样创建线程安全的方法

    面试问题: 下面的方法是否线程安全?怎样让它成为线程安全的方法? class MyCounter { private static int counter = 0; public static int ...

  5. 在unix系统下的 .o文件 .a文件 .so文件说明和相互关系

    .o文件 .o文件就是对象文件,包含编译好的可执行代码,当程序执行时,被链接库链接调用[相当于windows里的obj文件] .a文件unix中的静态链接库,包含多个需要包含的.o文件,主要特点是在 ...

  6. Apache的Order Allow,Deny 详解

    Allow和Deny可以用于apache的conf文件或者.htaccess文件中(配合Directory, Location, Files等),用来控制目录和文件的访问授权. 所以,最常用的是: O ...

  7. msysGit管理GitHub代码

    msysGit管理GitHub代码   代码的管理,在日常开发中是很重要的环节,程序员的修炼三部曲——版本控制,单元测试,项目自动化. 本篇就简单的说说通过msysGit来管理GitHub中的代码,实 ...

  8. Windows10 利用 Docker 配置 TensofFlow 深度学习工具

    TensorFlow 这个不用多介绍了吧,大家都知道,Google的开源深度学习软件库,官网点这里:https://www.tensorflow.org/ 当然这个工具官方支持装在 Ubuntu 和 ...

  9. linux下 ^M

    在Linux下使用vi来查看一些在Windows下创建的文本文件,有时会发现在行尾有一些“^M”.有几种方法可以处理. 注意:在Linux下,可以通过ctrl+v,ctrl+m,打出^M字符.而却,以 ...

  10. ASP.NET Web API 安全筛选器

    原文:https://msdn.microsoft.com/zh-cn/magazine/dn781361.aspx 身份验证和授权是应用程序安全的基础.身份验证通过验证提供的凭据来确定用户身份,而授 ...