本文主要介绍利用CoreGraphics和CADisplayLink来实现一个注水动画。来一个效果图先:

  

  在介绍注水动画前,先介绍利用CoreGraphics实现进度条的绘制。

  一、扇形进度绘制

  效果:

  代码如下:

- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
self.arcColor = [UIColor cyanColor];
}
return self;
} - (void)drawRect:(CGRect)rect {
[super drawRect:rect]; CGContextRef context = UIGraphicsGetCurrentContext(); [self.arcColor setFill]; CGFloat startAngle = -M_PI_2;
CGFloat endAngle = self.progress * M_PI * + startAngle; CGPoint center = CGPointMake(rect.size.width / , rect.size.height / ); UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:rect.size.width / startAngle:startAngle endAngle:endAngle clockwise:YES]; CGContextAddPath(context, path.CGPath);
CGContextAddLineToPoint(context, center.x, center.y); CGContextDrawPath(context, kCGPathFill);
} - (void)setProgress:(CGFloat)progress
{
NSLog(@"%g", progress);
if (progress > ) {
progress = ;
}else if (progress < ){
progress = ;
}
_progress = progress;
dispatch_async(dispatch_get_main_queue(), ^{
[self setNeedsDisplay];
});
}

原理就是根据不同的进度值不停的重新绘制扇形。

  二、绘制带边缘的扇形进度图

  

  代码如下:

@implementation ArcWithTrackProgressView

- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
self.backgroundColor = [UIColor clearColor];
self.trackColor = [UIColor cyanColor];
self.progressColor = [UIColor cyanColor];
}
return self;
} - (void)drawRect:(CGRect)rect
{
//绘制圈
UIBezierPath *trackPath = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(rect, , )];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 0.5);
CGContextAddPath(context, trackPath.CGPath); [self.trackColor setStroke];
CGContextDrawPath(context, kCGPathStroke);//绘制进度
[self.progressColor setFill];
CGFloat startAngle = - M_PI_2;
CGFloat endAngle = self.progress * * M_PI + startAngle;
CGPoint center = CGPointMake(CGRectGetWidth(rect) / , CGRectGetHeight(rect) / );
CGFloat radius = CGRectGetHeight(rect) / - ;//设置半径
UIBezierPath *progressPath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES]; CGContextAddPath(context, progressPath.CGPath);
CGContextAddLineToPoint(context, center.x, center.y);
CGContextDrawPath(context, kCGPathFill);
} - (void)setProgress:(CGFloat)progress
{
NSLog(@"%g", progress);
if (progress > ) {
progress = ;
}else if (progress < ){
progress = ;
}
_progress = progress;
dispatch_async(dispatch_get_main_queue(), ^{
[self setNeedsDisplay];
});
} @end

  三、绘制一个圆环进度

  效果图如下:

  

  此效果分为两步实现,一部分是绘制圆环,一部分是绘制勾。我在这里使用的CoreGraphics来绘制环,勾的话是利用CAShapeLayer来实现的。代码如下:

  

@implementation AnnularProgressView

- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self commonSetup];
}
return self;
} - (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
[self commonSetup];
}
return self;
} - (void)commonSetup
{
self.arcColor = [UIColor cyanColor];
self.lineWidth = .f; //设置shapeLayer
CAShapeLayer *tick = [[CAShapeLayer alloc] init];
tick.bounds = self.bounds;
tick.position = CGPointMake(CGRectGetWidth(self.bounds) / , CGRectGetHeight(self.bounds) / );
CGFloat width = CGRectGetWidth(self.bounds);
CGFloat height = CGRectGetHeight(self.bounds);
UIBezierPath *bezierPath = [[UIBezierPath alloc] init];
[bezierPath moveToPoint:CGPointMake(width * 0.25, height * 0.46)];
[bezierPath addLineToPoint:CGPointMake(width * 0.45, height * 0.71)];
[bezierPath addLineToPoint:CGPointMake(width * 0.78, height * 0.29)];
tick.path = bezierPath.CGPath;
tick.fillColor = [UIColor clearColor].CGColor;
tick.strokeColor = [UIColor cyanColor].CGColor;
tick.strokeStart = ;
tick.strokeEnd = ;
tick.lineWidth = self.lineWidth;
tick.lineCap = kCALineJoinRound; [self.layer addSublayer:tick]; self.tick = tick;
} - (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, self.lineWidth); [self.arcColor setStroke]; //绘制圆环
CGFloat startAngle = -M_PI_2;
CGFloat endAngle = self.progress * M_PI * + startAngle;
CGPoint center = CGPointMake(rect.size.width / , rect.size.height / );
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:rect.size.width / - self.lineWidth startAngle:startAngle endAngle:endAngle clockwise:YES];
CGContextAddPath(context, path.CGPath);
CGContextDrawPath(context, kCGPathStroke); self.tick.strokeEnd = self.progress;//设置勾的进度 } - (void)setProgress:(CGFloat)progress
{
NSLog(@"%g", progress);
if (progress > ) {
progress = ;
}else if (progress < ){
progress = ;
}
_progress = progress;
dispatch_async(dispatch_get_main_queue(), ^{
[self setNeedsDisplay];
});
}

  四、注水动画

  效果:

  

  注水动画的实现主要是通过正余弦函数绘制来实现的。正余弦曲线公式如下:

  正弦函数  

y=Asin(ωx+φ)+k //正弦函数
y=Acos(ωx+φ)+k //余弦函数

其中

A——振幅,当物体作轨迹符合正弦曲线的直线往复运动时,其值为行程的1/2。
(ωx+φ)——相位,反映变量y所处的状态。
φ——初相,x=0时的相位;反映在坐标系上则为图像的左右移动。
k——偏距,反映在坐标系上则为图像的上移或下移。
ω——角速度, 控制正弦周期(单位角度内震动的次数)。
  介绍完公式,接下来直接上代码:

@interface WaveProgressView ()

@property (nonatomic, assign) CGFloat initialPhase;//初相
@property (nonatomic, strong) CADisplayLink *timer; @end //y=Asin(ωx+φ)+k
@implementation WaveProgressView - (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
[self commonSetup];
}
return self;
} - (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self commonSetup];
}
return self;
} - (void)commonSetup
{
CADisplayLink *timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(moveWave:)];
[timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; self.backgroundColor = [UIColor clearColor];
} - (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext(); UIBezierPath *sinPath = [UIBezierPath bezierPath];
UIBezierPath *cosPath = [UIBezierPath bezierPath];
CGFloat y;
CGFloat amplitude = ;//振幅
CGFloat palstance = M_PI / self.bounds.size.width;//角速度 CGPoint startPoint = CGPointMake(, CGRectGetHeight(rect));
[sinPath moveToPoint:startPoint];
[cosPath moveToPoint:startPoint];
//正弦曲线
for (CGFloat x = 0.0 ; x <= rect.size.width; x++) {
y = amplitude * sin(palstance * x + self.initialPhase);
CGPoint point = CGPointMake(x, y + CGRectGetHeight(rect) * ( - self.progress) - amplitude); [sinPath addLineToPoint:point];
} //余弦曲线
for (int x = ; x <= rect.size.width; x++) {
y = amplitude * cos(palstance * x + self.initialPhase);
CGPoint point = CGPointMake(x, y + CGRectGetHeight(rect) * ( - self.progress) - amplitude); [cosPath addLineToPoint:point];
} CGPoint endPoint = CGPointMake(CGRectGetWidth(rect), CGRectGetHeight(rect));
[sinPath addLineToPoint:endPoint];
[cosPath addLineToPoint:endPoint];
[[UIColor lightGrayColor] setFill]; CGContextAddPath(context, sinPath.CGPath);
CGContextDrawPath(context, kCGPathFill); [[UIColor cyanColor] setFill];
CGContextAddPath(context, cosPath.CGPath);
CGContextDrawPath(context, kCGPathFill);
} - (void)moveWave:(CADisplayLink *)timer
{
self.initialPhase += 0.1; dispatch_async(dispatch_get_main_queue(), ^{
[self setNeedsDisplay];
}); } - (void)setProgress:(CGFloat)progress
{
NSLog(@"%g", progress);
if (progress > ) {
progress = ;
}else if (progress < ){
progress = ;
}
_progress = progress;
} - (void)dealloc
{
[self.timer invalidate];
}

  实现原理:设定好曲线的振幅、角速度,然后根据progress来设置正余弦曲线的绘制路径。利用CADisplayLink来不断的改变曲线的初相来达到曲线移动的效果。

  你可以从这里下载demo

iOS 动画篇 (三) CADisplayLink与CoreGraphics实现动画的更多相关文章

  1. 自己定义控件三部曲之动画篇(十三)——实现ListView Item进入动画

    前言:宝剑锋从磨砺出,梅花香自苦寒来 相关文章: <Android自己定义控件三部曲文章索引>: http://blog.csdn.net/harvic880925/article/det ...

  2. iOS开发UI篇—iOS开发中三种简单的动画设置

    iOS开发UI篇—iOS开发中三种简单的动画设置 [在ios开发中,动画是廉价的] 一.首尾式动画 代码示例: // beginAnimations表示此后的代码要“参与到”动画中 [UIView b ...

  3. iOS 动画篇 (二) CAShapeLayer与CoreAnimation结合使用

    接上一篇博客 iOS 动画篇(一) Core Animation CAShapeLayer是CALayer的一个子类,使用这个类能够很轻易实现曲线的动画. 先来一个折线动画效果: 示例代码: //1. ...

  4. iOS 开发之动画篇 - 从 UIView 动画说起

    毋庸置疑的:在iOS开发中,制作动画效果是最让开发者享受的环节之一.一个设计严谨.精细的动画效果能给用户耳目一新的效果,吸引他们的眼光 —— 这对于app而言是非常重要的. 本文作为动画文集的第一篇, ...

  5. iOS开发--动画篇之layout动画深入

    "不得不说,单单是文章的标题,可能不足以说明本文的内容.因此,在继续讲述约束动画之前,我先放上本文要实现的动画效果." 编辑:Bison投稿:Sindri的小巢 约束动画并不是非常 ...

  6. iOS动画篇:UIView动画

    iOS的动画效果一直都很棒很,给人的感觉就是很炫酷很流畅,起到增强用户体验的作用.在APP开发中实现动画效果有很多种方式,对于简单的应用场景,我们可以使用UIKit提供的动画来实现. UIView动画 ...

  7. iOS开发——动画篇Swift篇&动画效果的实现

    Swift - 动画效果的实现   在iOS中,实现动画有两种方法.一个是统一的animateWithDuration,另一个是组合出现的beginAnimations和commitAnimation ...

  8. iOS 动画篇 之 Core Animation (一)

    iOS中实现动画有两种方式,一种是自己不断的通过drawRect:方法来绘制,另外一种就是使用核心动画(Core Animation). 导语: 核心动画提供高帧速率和流畅的动画,而不会增加CPU的负 ...

  9. iOS动画篇:核心动画

    转:http://www.cocoachina.com/ios/20160517/16290.html 基本概念 1.什么是核心动画 Core Animation(核心动画)是一组功能强大.效果华丽的 ...

随机推荐

  1. android 程序执行linux命令注意事项

    一:问题描述    在已经root过的android设备下,app执行一个linux命令,app需要获取su权限,在某些android主板下会出现异常, Command: [su] Working D ...

  2. mysql千万级数据表,创建表及字段扩展的几条建议

    一:概述 当我们设计一个系统时,需要考虑到系统的运行一段时间后,表里数据量大约有多少,如果在初期,就能估算到某几张表数据量非常庞大时(比如聊天消息表),就要把表创建好,这篇文章从创建表,增加数据,以及 ...

  3. 合并查询结果集UNION(去重), UNION ALL(不去重),INTERSECT(交集),MINUS(差集,第一个结果集减去第二个结果集,第一个结果集中不在第二个结果集中的记录行),[NOT] EXIST

    MINUS配合[NOT] EXIST使用可以查询出包含符合某个条件的多记录的其他记录, 举例: 顾客A买了商品2.4.6 顾客B买了商品1.2.4 顾客C买了商品4.6 顾客D买了商品1.2.4.6 ...

  4. 【python】for循环

    >>> exp='welcom to python'>>> for i in exp: print(i,end=' ') w e l c o m t o p y t ...

  5. IOS学习2——Xcode快捷键大全

    转载自:图文解释XCode常用快捷键的使用 刚开始用Xcode是不是发现以前熟悉的开发环境的快捷键都不能用了?怎么快捷运行,停止,编辑等等.都不一样了.快速的掌握这些快捷键,能提供开发的效率. 其实快 ...

  6. NSQ之粗读浅谈

    回顾: 以前一直是C++开发(客户端),最近听同事讲go语言不错,随后便决定先从go语法开始投向go的怀抱.由于历史原因学习go语法时,用了半天的时间看完了菜鸟教程上相关资料,后来又看了易百教程上的一 ...

  7. css em单位

    本文同时发表在https://github.com/zhangyachen/zhangyachen.github.io/issues/41 为什么要有em 为了弹性布局.更准确的说是界面元素根据浏览器 ...

  8. HTML5 矩阵变换

    transforms 使用图形上下文对象的transforms方法修改变换矩阵,该方法的定义如下: context.transform(m11,m12,m21,m22,dx,dy); 其中m11,m1 ...

  9. 4.sass的分支结构、循环结构、函数

    分支结构 在sass里,可以使用@if让我们根据一些条件来应用特定的样式 结构: @if 条件 { } 如果条件为真的话,括号里的代码就会释放出来 例如: $use-refixes:true; .ro ...

  10. css3整理-方便查询使用

    最近详细地研究了CSS3的相关内容,并整理了这个文档,方便以后查询使用,分享给大家. 案例代码大家可以下载参考下:https://gitee.com/LIULIULIU8/CSS3 1.边框属性bor ...