这一周本来是想要再看下SceneKit方面的内容的, 不过接到一个引导页动画的需求, 对于好久不敲OC代码的我, 其实内心是很抵触的, 看了设计给的用AE的动画效果, 诶… 一个引导页搞那么复杂, 也不看看BAT三巨头和TMD独角兽的引导页不都是简单的图片咩…
动画分析
作为技术, 有需求就一定要实现, 其实很想拒绝这种伪需求的, 但是怎么办呢, 怼回去显得技术能力差似的, 好吧, 我们来看看设计给的一些素材来进一步分析实现呗…
好吧, 我们来分析一下如何分析这个动画吧, 动画一共有14个元素, 主要分为五步动画, 1. 圆形背景弹出动画, 2. 手机向上位移动画, 3, 手指点触动画, 4, 四个圆形弹出动画, 5 文字弹出, 光效位移, 圆形无规则旋转动画. 然后设计抛出一张牛逼闪闪的帧率图…
什么鬼, 就给些资源, 不告诉我控件摆放的位置, 不告诉我动画持续的时间, 这就把我打发了, 图片还是给我1080P的, 你是做的爽了, 我还要做屏幕适配呢, 感觉是在逗我呀. 算了, 我忍, 还是想想技术选型吧…
iOS动画方面可以选择UIView的动画, Core Animation核心动画, iOS10 属性动画, 因为是公司项目, 所有iOS10新的API肯定是不能用的, UIView的动画做不了手机弹出的那个蒙层效果, 接下来能够选的就只有Core Animation核心动画了, 核心动画是通过操作图层来实现动画, 但所有的动画都是假象, 假象就假象吧, 能实现就好呗… 核心动画就决定是你了!!
动画布局
制作动画效果, 我们肯定是需要将所有的元素都先放置在界面上, 先做屏幕适配为妙.
@property (nonatomic,strong) UIImageView * backgroundView; //圆形的背景图
@property (nonatomic,strong) UIImageView * phoneView; //手机
@property (nonatomic,strong) CALayer * maskLayer; //蒙层
@property (nonatomic,strong) CALayer * phoneLayer; //手机层
@property (nonatomic,strong) UIImageView * handView; //手指
@property (nonatomic,strong) UIImageView * homeView; //家
@property (nonatomic,strong) UIImageView * topIcon; //上面的圆
@property (nonatomic,strong) UIImageView * leftIcon; //左边的圆
@property (nonatomic,strong) UIImageView * bottomIcon; //下面的圆
@property (nonatomic,strong) UIImageView * rightIcon; //右面的圆
@property (nonatomic,strong) UIImageView * coverView; //光效
@property (nonatomic,strong) UIImageView * charView1; //文字1
@property (nonatomic,strong) UIImageView * charView2; //文字2
@property (nonatomic,strong) UIImageView * lineView1; //线1
@property (nonatomic,strong) UIImageView * lineView2; //线2
所有的控件都通过懒加载创建, 篇幅原因省略…
- (void)setupSubviews {
[self addSubview:self.backgroundView];
[self addSubview:self.coverView];
[self.backgroundView addSubview:self.phoneView];
[self.phoneView.layer addSublayer:self.phoneLayer]; //将手机展示层添加入View
[self addSubview:self.handView];
[self addSubview:self.homeView];
[self addSubview:self.topIcon];
[self addSubview:self.leftIcon];
[self addSubview:self.bottomIcon];
[self addSubview:self.rightIcon];
[self addSubview:self.charView1];
[self addSubview:self.charView2];
[self addSubview:self.lineView1];
[self addSubview:self.lineView2];
UIButton * btn = [UIButton buttonWithType:UIButtonTypeContactAdd]; //创建一个测试的动画执行按钮
btn.frame = CGRectMake(0, 100, 100, 100);
[btn addTarget:self action:@selector(loadAnimations) forControlEvents:64];
[self addSubview:btn];
}
通过layoutSubviews布局子控件, 由于篇幅原因, 仅展示部分代码
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat width = self.width * 0.75; // 由于设计不给标尺寸, 我们就根据屏幕的宽度比来确定对照比例.
CGFloat height = width * 1.10704961; //然后通过计算机算出比例 .... 真是醉了....
CGFloat length = width * 0.82114883;
_backgroundView.bounds = CGRectMake(0, 0, width, height);
_backgroundView.center = (CGPoint){self.center.x, self.center.y - 20};
CGFloat coverViewW = width * 1.06396867;
CGFloat coverViewH = coverViewW * 1.00736196;
_coverView.bounds = CGRectMake(0, 0, coverViewW, coverViewH);
_coverView.center = _backgroundView.center;
CGFloat phoneViewW = length * 0.68203498;
CGFloat phoneViewH = phoneViewW * 1.44988345;
CGFloat phoneViewX = (width - phoneViewW) * 0.5;
CGFloat phoneViewY = (height - phoneViewH) * 0.5;
_phoneView.frame = CGRectMake(phoneViewX, phoneViewY, phoneViewW, phoneViewH);
CGFloat phoneLayerX = 0;
CGFloat phoneLayerY = phoneViewH;
CGFloat phoneLayerW = phoneViewW;
CGFloat phoneLayerH = phoneViewH;
_phoneLayer.frame = CGRectMake(phoneLayerX, phoneLayerY, phoneLayerW, phoneLayerH);
CGFloat maskLayerW = length;
CGFloat maskLayerH = length;
CGFloat maskLayerX = (phoneViewW - maskLayerW) * 0.5;
CGFloat maskLayerY = (phoneViewH - maskLayerH) * 0.5;
_maskLayer.frame = CGRectMake(maskLayerX, maskLayerY, maskLayerW, maskLayerH);
...
}
以上代码均可以通过之前写的代码生成器进行生成 –> 传送门
动画换算
角度和弧度的换算以及帧数和描述的换算. 由于AE导出显示为1秒25帧, 可得出一帧是0.04秒.
#define SQAngle(x) ((x) / 180.0f * M_PI)
#define SQFrame(x) ((x) * 0.04)
动画封装
1.手机弹出的时候下面一半动画显示的效果, 对于没看过Layer层API的同学肯定是感觉很难实现的
- (UIImageView *)phoneView {
if (!_phoneView) {
_phoneView = [UIImageView new];
_phoneView.layer.mask = self.maskLayer; //关键点, 将蒙版层赋值给蒙版属性. (alpha 通道 自行百度)
}
return _phoneView;
}
- (CALayer *)phoneLayer {
if (!_phoneLayer) {
_phoneLayer = [CALayer new];
_phoneLayer.contents = (__bridge id)[UIImage imageNamed:@"手机"].CGImage; //寄宿图方式渲染图层
}
return _phoneLayer;
}
- (CALayer *)maskLayer {
if (!_maskLayer) {
_maskLayer = [CALayer new];
_maskLayer.contents = (__bridge id)[UIImage imageNamed:@"圆-黑"].CGImage;
}
return _maskLayer;
}
这样phoneView的层次结构就变成了 phoneView > phoneLayer / maskLayer, 通过操纵phoneLayer, 就能够实现这个动画效果了.而maskLayer是固定Alpha全黑蒙版, 进行图像的穿透和遮蔽.
2.位移动画的封装
- (CABasicAnimation *)translationWithText:(UIView *)text {
CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath:@"position.y"];
animation.fromValue = @(text.center.y + kscaleDeviceLength(50));
animation.toValue = @(text.center.y);
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
return animation;
}
- (CABasicAnimation *)translationWithIcon:(UIView *)icon {
CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath:@"position"];
animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(CGRectGetMidX(_homeView.frame), CGRectGetMaxY(_homeView.frame))];
animation.toValue = [NSValue valueWithCGPoint:icon.center];
return animation;
}
3.旋转动画的封装
- (CAKeyframeAnimation *)rotateWithIcon:(UIView *)icon clockwise:(BOOL)clockwise {
UIBezierPath * path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(icon.center.x, icon.center.y) radius:(arc4random()%5) + 1 startAngle:SQAngle(0) endAngle:SQAngle(360) clockwise:clockwise]; 随机生成路径
CAKeyframeAnimation * animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
animation.duration = 1.0;
animation.path = path.CGPath; //根据路径生成动画
animation.repeatCount = MAXFLOAT; //永久动画
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
return animation;
}
4.渐变及缩放动画的封装
- (CABasicAnimation *)opacityAnimation {
CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
animation.fromValue = @(0);
animation.toValue = @(1);
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
return animation;
}
- (CABasicAnimation *)scaleAnimation {
CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
animation.fromValue = @(0);
animation.toValue = @(1);
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
return animation;
}
5.组合动画 将多种动画进行组合封装
- (CAAnimationGroup *)groupAnimation:(CABasicAnimation *)animation {
CAAnimationGroup * group = [CAAnimationGroup animation];
group.fillMode = kCAFillModeForwards;
group.removedOnCompletion = NO;
group.duration = SQFrame(10);
group.delegate = self;
group.animations = @[[self opacityAnimation], animation, [self scaleAnimation]];
return group;
}
动画制作
1.圆形背景弹出动画
CAKeyframeAnimation * backgroundViewAnim = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"]; //关键帧动画
backgroundViewAnim.values = @[@0.3, @1.2, @1.0];
backgroundViewAnim.keyTimes = @[@0, @0.6, @1.0];
backgroundViewAnim.fillMode = kCAFillModeForwards; //需要保留后一帧
backgroundViewAnim.removedOnCompletion = NO; //完成后不移除动画
backgroundViewAnim.duration = SQFrame(6); //6帧
backgroundViewAnim.delegate = self;
[_backgroundView.layer addAnimation:backgroundViewAnim forKey:@"animation1"];
CABasicAnimation * lineViewAnim = [self scaleAnimation];
lineViewAnim.duration = SQFrame(6);
[_lineView1.layer addAnimation:lineViewAnim forKey:nil];
[_lineView2.layer addAnimation:lineViewAnim forKey:nil];
2.手机向上位移动画
if ([_backgroundView.layer animationForKey:@"animation1"] == anim) {
CABasicAnimation * phoneLayerAnim = [CABasicAnimation animationWithKeyPath:@"position.y"];
phoneLayerAnim.fromValue = @(_phoneView.height + kscaleDeviceLength(180));
phoneLayerAnim.toValue = @(_phoneView.height - kscaleDeviceLength(70));
phoneLayerAnim.fillMode = kCAFillModeForwards;
phoneLayerAnim.removedOnCompletion = NO;
phoneLayerAnim.duration = SQFrame(10);
phoneLayerAnim.delegate = self;
[_phoneLayer addAnimation:phoneLayerAnim forKey:@"animation2"];
}
3.手指点触动画
if ([_phoneLayer animationForKey:@"animation2"] == anim) {
CABasicAnimation * translation = [CABasicAnimation animationWithKeyPath:@"position"];
translation.fromValue = [NSValue valueWithCGPoint:CGPointMake(_backgroundView.center.x + kscaleDeviceLength(50), _backgroundView.center.y + kscaleDeviceLength(60))];
translation.toValue = [NSValue valueWithCGPoint:CGPointMake(_backgroundView.center.x, _backgroundView.center.y + kscaleDeviceLength(30))];
CAAnimationGroup * group = [CAAnimationGroup animation]; //动画组
group.fillMode = kCAFillModeForwards;
group.removedOnCompletion = NO;
group.duration = SQFrame(19);
group.animations = @[translation, [self opacityAnimation]];
group.delegate = self;
[_handView.layer addAnimation:group forKey:@"animation3"];
}
4.四个圆形弹出动画
if ([_handView.layer animationForKey:@"animation3"] == anim) {
CABasicAnimation * animation = [self opacityAnimation];
animation.duration = SQFrame(5);
[_homeView.layer addAnimation:animation forKey:nil];
[_topIcon.layer addAnimation:[self groupAnimation:[self translationWithIcon:_topIcon]] forKey:@"animation4"];
[_rightIcon.layer addAnimation:[self groupAnimation:[self translationWithIcon:_rightIcon]] forKey:nil];
[_bottomIcon.layer addAnimation:[self groupAnimation:[self translationWithIcon:_bottomIcon]] forKey:nil];
[_leftIcon.layer addAnimation:[self groupAnimation:[self translationWithIcon:_leftIcon]] forKey:nil];
}
5.文字弹出, 光效位移, 圆形无规则旋转动画
if ([_topIcon.layer animationForKey:@"animation4"] == anim) {
[_topIcon.layer addAnimation:[self rotateWithIcon:_topIcon clockwise:YES] forKey:nil];
[_rightIcon.layer addAnimation:[self rotateWithIcon:_rightIcon clockwise:NO] forKey:nil];
[_bottomIcon.layer addAnimation:[self rotateWithIcon:_bottomIcon clockwise:YES] forKey:nil];
[_leftIcon.layer addAnimation:[self rotateWithIcon:_leftIcon clockwise:NO] forKey:nil];
CABasicAnimation * opacity = [self opacityAnimation];
opacity.duration = SQFrame(5);
CABasicAnimation * opacity2 = [self opacityAnimation];
opacity2.beginTime = SQFrame(5);
opacity2.duration = SQFrame(5);
CABasicAnimation * charView1Anim = [self translationWithText:_charView1];
charView1Anim.duration = SQFrame(10);
CABasicAnimation * charView2Anim = [self translationWithText:_charView2];
charView2Anim.beginTime = SQFrame(5);
charView2Anim.duration = SQFrame(10);
CAAnimationGroup * group = [CAAnimationGroup animation];
group.fillMode = kCAFillModeForwards;
group.removedOnCompletion = NO;
group.duration = SQFrame(15);
group.animations = @[opacity, charView1Anim];
CAAnimationGroup * group2 = [CAAnimationGroup animation];
group2.fillMode = kCAFillModeForwards;
group2.removedOnCompletion = NO;
group2.duration = SQFrame(15);
group2.animations = @[opacity2, charView2Anim];
[_charView1.layer addAnimation:group forKey:nil];
[_charView2.layer addAnimation:group2 forKey:nil];
CABasicAnimation * translation = [CABasicAnimation animationWithKeyPath:@"position"];
translation.fromValue = [NSValue valueWithCGPoint:CGPointMake(_coverView.center.x - kscaleDeviceLength(50), _coverView.center.y + kscaleDeviceLength(50))];
translation.toValue = [NSValue valueWithCGPoint:_coverView.center];
translation.duration = SQFrame(10);
CAAnimationGroup * group3 = [CAAnimationGroup animation];
group3.fillMode = kCAFillModeForwards;
group3.removedOnCompletion = NO;
group3.duration = SQFrame(10);
group3.animations = @[opacity, translation];
[_coverView.layer addAnimation:group3 forKey:nil];
}
动画效果
来看一下我们的程序跑起来的样子如何.
内存泄漏
由于动画部分并没有什么技术含量, 仅仅贴出源码, 就不上传github了. 但是这里要请大家来帮个忙, 找找我源码中的内存泄漏的部分, 因为没有走dealloc方法, 网上说CAAnimation的delegate 是strong修饰的导致循环引用, 释放不了就内存泄漏了, 但是如果将其释放了, animation.fillMode = kCAFillModeForwards;
和 animation.removedOnCompletion = NO;
就没有效果了, 动画效果就不能显示了, 所以说应该不是apple的坑, 但这种情况该如何解决呢??
解决内存泄漏
再次感谢@搬运工开发者提供的方法解决了内存泄漏的问题, 个中原因可以看他写的文章: http://www.jianshu.com/p/39462a71cf40, 简单来说就是先使用强引用保证动画不在运行时被释放, 再用弱引用保证不产生循环引用.
添加代理管理者, 进行自定义代理(作者说的)
@protocol HYAnimationWeakDelegate <NSObject>
@optional
- (void)animationDidStart:(CAAnimation *)anim;
- (void)animationDidStop:(CAAnimation *)anim;
@end
@interface HYAnimationDelegateManager : NSObject <CAAnimationDelegate>
@property (weak, nonatomic) id<HYAnimationWeakDelegate> delegate;
@end
@implementation HYAnimationDelegateManager
- (void)animationDidStart:(CAAnimation *)anim {
if (_delegate && [_delegate respondsToSelector:@selector(animationDidStart:)]) {
[_delegate animationDidStart:anim];
}
}
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
if (_delegate && [_delegate respondsToSelector:@selector(animationDidStop:)]) {
[_delegate animationDidStop:anim];
}
}
@end
如何使用:
HYAnimationDelegateManager * manager = [HYAnimationDelegateManager new]; //创建实例
manager.delegate = self; //进行弱引用
backgroundViewAnim.delegate = manager; //进行强引用
实测解决了内存泄漏的问题, delloc进行释放.
完整源码
//
// HYGuideView.m
// Mall
//
// Created by 双泉 朱 on 17/6/13.
// Copyright © 2017年 _Zhizi_. All rights reserved.
//
#import "HYGuideView.h"
#define SQAngle(x) ((x) / 180.0f * M_PI)
#define SQFrame(x) ((x) * 0.04)
@protocol HYAnimationWeakDelegate <NSObject>
@optional
- (void)animationDidStart:(CAAnimation *)anim;
- (void)animationDidStop:(CAAnimation *)anim;
@end
@interface HYAnimationDelegateManager : NSObject <CAAnimationDelegate>
@property (weak, nonatomic) id<HYAnimationWeakDelegate> delegate;
@end
@implementation HYAnimationDelegateManager
- (void)animationDidStart:(CAAnimation *)anim {
if (_delegate && [_delegate respondsToSelector:@selector(animationDidStart:)]) {
[_delegate animationDidStart:anim];
}
}
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
if (_delegate && [_delegate respondsToSelector:@selector(animationDidStop:)]) {
[_delegate animationDidStop:anim];
}
}
@end
@interface HYGuideView () <HYAnimationWeakDelegate>
@property (nonatomic,strong) UIImageView * backgroundView;
@property (nonatomic,strong) UIImageView * phoneView;
@property (nonatomic,strong) CALayer * maskLayer;
@property (nonatomic,strong) CALayer * phoneLayer;
@property (nonatomic,strong) UIImageView * handView;
@property (nonatomic,strong) UIImageView * homeView;
@property (nonatomic,strong) UIImageView * topIcon;
@property (nonatomic,strong) UIImageView * leftIcon;
@property (nonatomic,strong) UIImageView * bottomIcon;
@property (nonatomic,strong) UIImageView * rightIcon;
@property (nonatomic,strong) UIImageView * coverView;
@property (nonatomic,strong) UIImageView * charView1;
@property (nonatomic,strong) UIImageView * charView2;
@property (nonatomic,strong) UIImageView * lineView1;
@property (nonatomic,strong) UIImageView * lineView2;
@end
@implementation HYGuideView
- (void)dealloc {
NSLog(@"%@ - execute %s",NSStringFromClass([self class]),__func__);
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setupSubviews];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
[self setupSubviews];
}
return self;
}
- (UIImageView *)backgroundView {
if (!_backgroundView) {
_backgroundView = [UIImageView new];
_backgroundView.image = [UIImage imageNamed:@"背景"];
_backgroundView.transform = CGAffineTransformMakeScale(0.3, 0.3);
}
return _backgroundView;
}
- (UIImageView *)phoneView {
if (!_phoneView) {
_phoneView = [UIImageView new];
_phoneView.layer.mask = self.maskLayer;
}
return _phoneView;
}
- (CALayer *)phoneLayer {
if (!_phoneLayer) {
_phoneLayer = [CALayer new];
_phoneLayer.contents = (__bridge id)[UIImage imageNamed:@"手机"].CGImage;
}
return _phoneLayer;
}
- (CALayer *)maskLayer {
if (!_maskLayer) {
_maskLayer = [CALayer new];
_maskLayer.contents = (__bridge id)[UIImage imageNamed:@"圆-黑"].CGImage;
}
return _maskLayer;
}
- (UIImageView *)handView {
if (!_handView) {
_handView = [UIImageView new];
_handView.image = [UIImage imageNamed:@"手"];
_handView.layer.anchorPoint = CGPointZero;
_handView.layer.opacity = 0;
}
return _handView;
}
- (UIImageView *)homeView {
if (!_homeView) {
_homeView = [UIImageView new];
_homeView.image = [UIImage imageNamed:@"home"];
_homeView.layer.opacity = 0;
}
return _homeView;
}
- (UIImageView *)topIcon {
if (!_topIcon) {
_topIcon = [UIImageView new];
_topIcon.image = [UIImage imageNamed:@"全球购"];
_topIcon.transform = CGAffineTransformMakeScale(0, 0);
}
return _topIcon;
}
- (UIImageView *)leftIcon {
if (!_leftIcon) {
_leftIcon = [UIImageView new];
_leftIcon.image = [UIImage imageNamed:@"互助圈"];
_leftIcon.transform = CGAffineTransformMakeScale(0, 0);
}
return _leftIcon;
}
- (UIImageView *)bottomIcon {
if (!_bottomIcon) {
_bottomIcon = [UIImageView new];
_bottomIcon.image = [UIImage imageNamed:@"签到"];
_bottomIcon.transform = CGAffineTransformMakeScale(0, 0);
}
return _bottomIcon;
}
- (UIImageView *)rightIcon {
if (!_rightIcon) {
_rightIcon = [UIImageView new];
_rightIcon.image = [UIImage imageNamed:@"折"];
_rightIcon.transform = CGAffineTransformMakeScale(0, 0);
}
return _rightIcon;
}
- (UIImageView *)coverView {
if (!_coverView) {
_coverView = [UIImageView new];
_coverView.image = [UIImage imageNamed:@"光效"];
_coverView.layer.opacity = 0;
}
return _coverView;
}
- (UIImageView *)charView1 {
if (!_charView1) {
_charView1 = [UIImageView new];
_charView1.image = [UIImage imageNamed:@"全新首页"];
_charView1.layer.opacity = 0;
}
return _charView1;
}
- (UIImageView *)charView2 {
if (!_charView2) {
_charView2 = [UIImageView new];
_charView2.image = [UIImage imageNamed:@"让您体验不一样的医药电商"];
_charView2.layer.opacity = 0;
}
return _charView2;
}
- (UIImageView *)lineView1 {
if (!_lineView1) {
_lineView1 = [UIImageView new];
_lineView1.image = [UIImage imageNamed:@"line1"];
}
return _lineView1;
}
- (UIImageView *)lineView2 {
if (!_lineView2) {
_lineView2 = [UIImageView new];
_lineView2.image = [UIImage imageNamed:@"line2"];
}
return _lineView2;
}
- (void)setupSubviews {
[self addSubview:self.backgroundView];
[self addSubview:self.coverView];
[self.backgroundView addSubview:self.phoneView];
[self.phoneView.layer addSublayer:self.phoneLayer];
[self addSubview:self.handView];
[self addSubview:self.homeView];
[self addSubview:self.topIcon];
[self addSubview:self.leftIcon];
[self addSubview:self.bottomIcon];
[self addSubview:self.rightIcon];
[self addSubview:self.charView1];
[self addSubview:self.charView2];
[self addSubview:self.lineView1];
[self addSubview:self.lineView2];
UIButton * btn = [UIButton buttonWithType:UIButtonTypeContactAdd];
btn.frame = CGRectMake(0, 100, 100, 100);
[btn addTarget:self action:@selector(loadAnimations) forControlEvents:64];
[self addSubview:btn];
}
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat width = self.width * 0.75;
CGFloat height = width * 1.10704961;
CGFloat length = width * 0.82114883;
_backgroundView.bounds = CGRectMake(0, 0, width, height);
_backgroundView.center = (CGPoint){self.center.x, self.center.y - 20};
CGFloat coverViewW = width * 1.06396867;
CGFloat coverViewH = coverViewW * 1.00736196;
_coverView.bounds = CGRectMake(0, 0, coverViewW, coverViewH);
_coverView.center = _backgroundView.center;
CGFloat phoneViewW = length * 0.68203498;
CGFloat phoneViewH = phoneViewW * 1.44988345;
CGFloat phoneViewX = (width - phoneViewW) * 0.5;
CGFloat phoneViewY = (height - phoneViewH) * 0.5;
_phoneView.frame = CGRectMake(phoneViewX, phoneViewY, phoneViewW, phoneViewH);
CGFloat phoneLayerX = 0;
CGFloat phoneLayerY = phoneViewH;
CGFloat phoneLayerW = phoneViewW;
CGFloat phoneLayerH = phoneViewH;
_phoneLayer.frame = CGRectMake(phoneLayerX, phoneLayerY, phoneLayerW, phoneLayerH);
CGFloat maskLayerW = length;
CGFloat maskLayerH = length;
CGFloat maskLayerX = (phoneViewW - maskLayerW) * 0.5;
CGFloat maskLayerY = (phoneViewH - maskLayerH) * 0.5;
_maskLayer.frame = CGRectMake(maskLayerX, maskLayerY, maskLayerW, maskLayerH);
CGFloat handViewX = _backgroundView.center.x + kscaleDeviceLength(50);
CGFloat handViewY = _backgroundView.center.y + kscaleDeviceLength(60);
CGFloat handViewW = width * 0.56396867;
CGFloat handViewH = handViewW * 0.98611111;
_handView.frame = CGRectMake(handViewX, handViewY, handViewW, handViewH);
CGFloat homeViewW = width * 0.27154047;
CGFloat homeViewH = homeViewW * 1.02884615;
CGFloat homeViewX = _backgroundView.center.x - homeViewW * 0.5;
CGFloat homeViewY = _backgroundView.center.y - homeViewH * 0.5 + kscaleDeviceLength(15);
_homeView.frame = CGRectMake(homeViewX, homeViewY, homeViewW, homeViewH);
CGFloat topIconW = width * 0.32375979;
CGFloat topIconH = topIconW;
_topIcon.bounds = CGRectMake(0, 0, topIconW, topIconH);
_topIcon.center = CGPointMake(kscaleDeviceLength(200), kscaleDeviceLength(130));
CGFloat leftIconW = width * 0.28720627;
CGFloat leftIconH = leftIconW;
_leftIcon.bounds = CGRectMake(0, 0, leftIconW, leftIconH);
_leftIcon.center = CGPointMake(kscaleDeviceLength(40), kscaleDeviceLength(200));
CGFloat bottomIconW = width * 0.30417755;
CGFloat bottomIconH = bottomIconW;
_bottomIcon.bounds = CGRectMake(0, 0, bottomIconW, bottomIconH);
_bottomIcon.center = CGPointMake(kscaleDeviceLength(50), kscaleDeviceLength(360));
CGFloat rightIconW = width * 0.25195822;
CGFloat rightIconH = rightIconW;
_rightIcon.bounds = CGRectMake(0, 0, rightIconW, rightIconH);
_rightIcon.center = CGPointMake(kscaleDeviceLength(260), kscaleDeviceLength(260));
CGFloat charView2W = width * 0.94386423;
CGFloat charView2H = charView2W * 0.07745505;
CGFloat charView2X = (self.width - charView2W) * 0.5;
CGFloat charView2Y = self.height - charView2H - kscaleDeviceLength(50);
_charView2.frame = CGRectMake(charView2X, charView2Y, charView2W, charView2H);
CGFloat charView1W = width * 0.4308094;
CGFloat charView1H = charView1W * 0.23939394;
CGFloat charView1X = (self.width - charView1W) * 0.5;
CGFloat charView1Y = charView2Y - charView1H - kscaleDeviceLength(15);
_charView1.frame = CGRectMake(charView1X, charView1Y, charView1W, charView1H);
CGFloat lineView1X = kscaleDeviceLength(238.5);
CGFloat lineView1Y = kscaleDeviceLength(131);
CGFloat lineView1W = width * 0.07832898;
CGFloat lineView1H = lineView1W;
_lineView1.frame = CGRectMake(lineView1X, lineView1Y, lineView1W, lineView1H);
CGFloat lineView2X = kscaleDeviceLength(80);
CGFloat lineView2Y = kscaleDeviceLength(400);
CGFloat lineView2W = lineView1W;
CGFloat lineView2H = lineView1W;
_lineView2.frame = CGRectMake(lineView2X, lineView2Y, lineView2W, lineView2H);
}
- (void)loadAnimations {
CAKeyframeAnimation * backgroundViewAnim = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"];
backgroundViewAnim.values = @[@0.3, @1.2, @1.0];
backgroundViewAnim.keyTimes = @[@0, @0.6, @1.0];
backgroundViewAnim.fillMode = kCAFillModeForwards;
backgroundViewAnim.removedOnCompletion = NO;
backgroundViewAnim.duration = SQFrame(6);
HYAnimationDelegateManager * manager = [HYAnimationDelegateManager new];
manager.delegate = self;
backgroundViewAnim.delegate = manager;
[_backgroundView.layer addAnimation:backgroundViewAnim forKey:@"animation1"];
CABasicAnimation * lineViewAnim = [self scaleAnimation];
lineViewAnim.duration = SQFrame(6);
[_lineView1.layer addAnimation:lineViewAnim forKey:nil];
[_lineView2.layer addAnimation:lineViewAnim forKey:nil];
}
- (void)animationDidStop:(CAAnimation *)anim {
if ([_backgroundView.layer animationForKey:@"animation1"] == anim) {
CABasicAnimation * phoneLayerAnim = [CABasicAnimation animationWithKeyPath:@"position.y"];
phoneLayerAnim.fromValue = @(_phoneView.height + kscaleDeviceLength(180));
phoneLayerAnim.toValue = @(_phoneView.height - kscaleDeviceLength(70));
phoneLayerAnim.fillMode = kCAFillModeForwards;
phoneLayerAnim.removedOnCompletion = NO;
phoneLayerAnim.duration = SQFrame(10);
HYAnimationDelegateManager * manager = [HYAnimationDelegateManager new];
manager.delegate = self;
phoneLayerAnim.delegate = manager;
[_phoneLayer addAnimation:phoneLayerAnim forKey:@"animation2"];
}
if ([_phoneLayer animationForKey:@"animation2"] == anim) {
CABasicAnimation * translation = [CABasicAnimation animationWithKeyPath:@"position"];
translation.fromValue = [NSValue valueWithCGPoint:CGPointMake(_backgroundView.center.x + kscaleDeviceLength(50), _backgroundView.center.y + kscaleDeviceLength(60))];
translation.toValue = [NSValue valueWithCGPoint:CGPointMake(_backgroundView.center.x, _backgroundView.center.y + kscaleDeviceLength(30))];
CAAnimationGroup * group = [CAAnimationGroup animation];
group.fillMode = kCAFillModeForwards;
group.removedOnCompletion = NO;
group.duration = SQFrame(19);
group.animations = @[translation, [self opacityAnimation]];
HYAnimationDelegateManager * manager = [HYAnimationDelegateManager new];
manager.delegate = self;
group.delegate = manager;
[_handView.layer addAnimation:group forKey:@"animation3"];
}
if ([_handView.layer animationForKey:@"animation3"] == anim) {
CABasicAnimation * animation = [self opacityAnimation];
animation.duration = SQFrame(5);
[_homeView.layer addAnimation:animation forKey:nil];
[_topIcon.layer addAnimation:[self groupAnimation:[self translationWithIcon:_topIcon]] forKey:@"animation4"];
[_rightIcon.layer addAnimation:[self groupAnimation:[self translationWithIcon:_rightIcon]] forKey:nil];
[_bottomIcon.layer addAnimation:[self groupAnimation:[self translationWithIcon:_bottomIcon]] forKey:nil];
[_leftIcon.layer addAnimation:[self groupAnimation:[self translationWithIcon:_leftIcon]] forKey:nil];
}
if ([_topIcon.layer animationForKey:@"animation4"] == anim) {
[_topIcon.layer addAnimation:[self rotateWithIcon:_topIcon clockwise:YES] forKey:nil];
[_rightIcon.layer addAnimation:[self rotateWithIcon:_rightIcon clockwise:NO] forKey:nil];
[_bottomIcon.layer addAnimation:[self rotateWithIcon:_bottomIcon clockwise:YES] forKey:nil];
[_leftIcon.layer addAnimation:[self rotateWithIcon:_leftIcon clockwise:NO] forKey:nil];
CABasicAnimation * opacity = [self opacityAnimation];
opacity.duration = SQFrame(5);
CABasicAnimation * opacity2 = [self opacityAnimation];
opacity2.beginTime = SQFrame(5);
opacity2.duration = SQFrame(5);
CABasicAnimation * charView1Anim = [self translationWithText:_charView1];
charView1Anim.duration = SQFrame(10);
CABasicAnimation * charView2Anim = [self translationWithText:_charView2];
charView2Anim.beginTime = SQFrame(5);
charView2Anim.duration = SQFrame(10);
CAAnimationGroup * group = [CAAnimationGroup animation];
group.fillMode = kCAFillModeForwards;
group.removedOnCompletion = NO;
group.duration = SQFrame(15);
group.animations = @[opacity, charView1Anim];
CAAnimationGroup * group2 = [CAAnimationGroup animation];
group2.fillMode = kCAFillModeForwards;
group2.removedOnCompletion = NO;
group2.duration = SQFrame(15);
group2.animations = @[opacity2, charView2Anim];
[_charView1.layer addAnimation:group forKey:nil];
[_charView2.layer addAnimation:group2 forKey:nil];
CABasicAnimation * translation = [CABasicAnimation animationWithKeyPath:@"position"];
translation.fromValue = [NSValue valueWithCGPoint:CGPointMake(_coverView.center.x - kscaleDeviceLength(50), _coverView.center.y + kscaleDeviceLength(50))];
translation.toValue = [NSValue valueWithCGPoint:_coverView.center];
translation.duration = SQFrame(10);
CAAnimationGroup * group3 = [CAAnimationGroup animation];
group3.fillMode = kCAFillModeForwards;
group3.removedOnCompletion = NO;
group3.duration = SQFrame(10);
group3.animations = @[opacity, translation];
[_coverView.layer addAnimation:group3 forKey:nil];
}
}
- (CABasicAnimation *)translationWithText:(UIView *)text {
CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath:@"position.y"];
animation.fromValue = @(text.center.y + kscaleDeviceLength(50));
animation.toValue = @(text.center.y);
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
return animation;
}
- (CABasicAnimation *)translationWithIcon:(UIView *)icon {
CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath:@"position"];
animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(CGRectGetMidX(_homeView.frame), CGRectGetMaxY(_homeView.frame))];
animation.toValue = [NSValue valueWithCGPoint:icon.center];
return animation;
}
- (CAKeyframeAnimation *)rotateWithIcon:(UIView *)icon clockwise:(BOOL)clockwise {
UIBezierPath * path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(icon.center.x, icon.center.y) radius:(arc4random()%5) + 1 startAngle:SQAngle(0) endAngle:SQAngle(360) clockwise:clockwise];
CAKeyframeAnimation * animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
animation.duration = 1.0;
animation.path = path.CGPath;
animation.repeatCount = MAXFLOAT;
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
return animation;
}
- (CAAnimationGroup *)groupAnimation:(CABasicAnimation *)animation {
CAAnimationGroup * group = [CAAnimationGroup animation];
group.fillMode = kCAFillModeForwards;
group.removedOnCompletion = NO;
group.duration = SQFrame(10);
HYAnimationDelegateManager * manager = [HYAnimationDelegateManager new];
manager.delegate = self;
group.delegate = manager;
group.animations = @[[self opacityAnimation], animation, [self scaleAnimation]];
return group;
}
- (CABasicAnimation *)opacityAnimation {
CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
animation.fromValue = @(0);
animation.toValue = @(1);
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
return animation;
}
- (CABasicAnimation *)scaleAnimation {
CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
animation.fromValue = @(0);
animation.toValue = @(1);
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
return animation;
}
@end