Github:https://github.com/lxypeter/CYLaunchAnimateViewController
很多如新浪微博,閑魚(yú)等常用App在啟動(dòng)后都會(huì)有一個(gè)啟動(dòng)頁(yè)的延伸您访,作為廣告頁(yè)或介紹頁(yè)使用。仿照這種模式封裝了一個(gè)Controller驱犹,簡(jiǎn)便地實(shí)現(xiàn)這種帶動(dòng)畫(huà)的延伸頁(yè)面鸵贬。
Demo
現(xiàn)在主要提供了如下幾種動(dòng)畫(huà)效果提供使用脚线。具體可參照CYLaunchAnimateViewController.h 中的枚舉宣蠕。
如何使用
作為啟動(dòng)頁(yè)的延伸例隆,具體的調(diào)用應(yīng)該在 AppDelegate.m中完成。更多具體的客制化參數(shù)可到CYLaunchAnimateViewController.h
中查看植影。
- 導(dǎo)入
CYLaunchAnimateViewController.h
#import "CYLaunchAnimateViewController.h"
- 初始化延伸啟動(dòng)頁(yè)View
//just an example
UIView *launchView = [[UIView alloc]initWithFrame:[UIScreen mainScreen].bounds];
/*
* launchView setting...
*/
- 初始化CYLaunchAnimateViewController裳擎,并調(diào)用
show
方法涎永。(在調(diào)用show
方法展示前請(qǐng)確保keyWindow已經(jīng)設(shè)置)
CYLaunchAnimateViewController *launchController = [[CYLaunchAnimateViewController alloc]initWithContentView:launchView animateType:CYLaunchAnimateTypePointZoomOut showSkipButton:YES];
[launchController show];
注意contentView并不一定要是屏幕大小思币,任意大小的View都可作為延伸啟動(dòng)頁(yè)View鹿响,CYLaunchAnimateViewController會(huì)自動(dòng)將View設(shè)置到屏幕中央。
實(shí)現(xiàn)思路
CYLaunchAnimateViewController應(yīng)該提供的是任意延伸頁(yè)面的展示以及消失時(shí)的動(dòng)畫(huà)效果谷饿,因而實(shí)現(xiàn)主要分為三步:
- 1.延伸頁(yè)面的設(shè)置惶我;
- 2.延伸頁(yè)面的展示;
- 3.結(jié)束動(dòng)畫(huà)效果的實(shí)現(xiàn)博投;
1.延伸頁(yè)面的設(shè)置
為適配任意大小的延伸頁(yè)绸贡,在加載頁(yè)面時(shí)將控制器view
的backgroundColor
設(shè)為透明色并將自定義的延伸頁(yè)作為subView
設(shè)于屏幕正中。
- (void)configureSubViews{
NSAssert(_contentView, @"contentView must not be nil!");
self.view.backgroundColor = [UIColor clearColor];
_contentView.center = CGPointMake(self.view.center.x, self.view.center.y);
[self.view addSubview:_contentView];
if(self.showSkipButton){
self.skipButton.remainSec = _waitDuration;
[self.view addSubview:self.skipButton];
[self.skipButton addTarget:self action:@selector(dismissAtOnce) forControlEvents:UIControlEventTouchUpInside];
}
}
2.延伸頁(yè)面的展示
作為啟動(dòng)頁(yè)的延伸毅哗,延伸頁(yè)面需要緊跟靜態(tài)啟動(dòng)頁(yè)并在在根控制器視圖上層(淡出動(dòng)畫(huà)所需)听怕。因?yàn)橐WC延伸頁(yè)置于根控制器上層,網(wǎng)上有另一種實(shí)現(xiàn)方法是將延伸頁(yè)的設(shè)置和控制的代碼置于根控制器的生命周期之中虑绵,雖然可以實(shí)現(xiàn)相同的效果尿瞭,但耦合性較強(qiáng)并不方便組件化,所以我采用了另一種方式——將延伸頁(yè)面直接添加到程序已經(jīng)初始化的keyWindow之上翅睛。
- (void)show{
UIWindow *window = [UIApplication sharedApplication].keyWindow;
NSAssert(window,@"keyWindow must be init!");
[window addSubview:self.view];
}
當(dāng)頁(yè)面加載完成声搁,初始化計(jì)時(shí)器NSTimer
,開(kāi)始倒計(jì)時(shí)捕发。有限的支持有小數(shù)位的等待時(shí)間疏旨,每0.1秒觸發(fā)一次- (void)countDown
方法,將誤差控制在0.1秒以內(nèi)扎酷。
- (void)configureTimer{
self.timePass = 0.0;
NSTimer *timer = [NSTimer timerWithTimeInterval:0.1 target:self selector:@selector(countDown) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode];
self.timer = timer;
[timer fire];
}
刷新skipButton
上的倒計(jì)時(shí)(如有)檐涝,判斷倒計(jì)時(shí)是否結(jié)束。結(jié)束時(shí)停止計(jì)時(shí)器霞玄,開(kāi)始結(jié)束動(dòng)畫(huà)骤铃。
- (void)countDown{
_timePass+=0.1;
_skipButton.remainSec = _waitDuration-_timePass<0?0:_waitDuration-_timePass;
if(_waitDuration<=_timePass){
[self.timer invalidate];
self.timer = nil;
[self dismiss];
}
}
3.結(jié)束動(dòng)畫(huà)效果的實(shí)現(xiàn)
結(jié)束動(dòng)畫(huà)就是CYLaunchAnimateViewController的view
的消失過(guò)程。以核心動(dòng)畫(huà)和CAShapeLayer為基礎(chǔ)坷剧,CYLaunchAnimateViewController自身作為動(dòng)畫(huà)的代理惰爬,監(jiān)控動(dòng)畫(huà)的周期。以CYLaunchAnimateTypeFade
和CYLaunchAnimateTypePointZoomIn1
兩種動(dòng)畫(huà)效果為例惫企。
- (void)dismiss{
switch (self.animateType){
case CYLaunchAnimateTypeFade:{
CABasicAnimation *animation = [CABasicAnimation animation];
animation.delegate = self;
[animation setDuration:_animateDuration];
animation.keyPath = @"opacity";
animation.toValue = @(0);
[self.view.layer addAnimation:animation forKey:nil];
break;
}
//...
case CYLaunchAnimateTypePointZoomIn1:{
CAShapeLayer *maskLayer = [CAShapeLayer layer];
self.view.layer.mask = maskLayer;
CGSize screenSize = [UIScreen mainScreen].bounds.size;
CAKeyframeAnimation *keyFrameAnimation = [CAKeyframeAnimation animationWithKeyPath:@"path"];
[keyFrameAnimation setDuration:_animateDuration];
keyFrameAnimation.delegate = self;
UIBezierPath *pathOne = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.view.center.x, self.view.center.y) radius:hypot(screenSize.height, screenSize.width)/2 startAngle:0 endAngle:2 * M_PI clockwise:NO];
UIBezierPath *pathTwo = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.view.center.x, self.view.center.y) radius:screenSize.width/2*0.5 startAngle:0 endAngle:2 * M_PI clockwise:NO];
UIBezierPath *pathThree = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.view.center.x, self.view.center.y) radius:screenSize.width/2*0.7 startAngle:0 endAngle:2 * M_PI clockwise:NO];
UIBezierPath *pathFour = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.view.center.x, self.view.center.y) radius:1 startAngle:0 endAngle:2 * M_PI clockwise:NO];
keyFrameAnimation.values = @[(__bridge id)(pathOne.CGPath),(__bridge id)(pathTwo.CGPath),(__bridge id)(pathThree.CGPath),(__bridge id)(pathFour.CGPath)];
keyFrameAnimation.keyTimes = @[@(0),@(0.5),@(0.9),@(1)];
[maskLayer addAnimation:keyFrameAnimation forKey:nil];
break;
}
//...
default:
break;
}
}
當(dāng)動(dòng)畫(huà)結(jié)束撕瞧,CYLaunchAnimateViewController的view
從keyWindow移除,執(zhí)行complete
block中內(nèi)容(如有)狞尔。
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
[self dismissAtOnce];
}
- (void)dismissAtOnce{
[self.timer invalidate];
self.timer = nil;
[self.view removeFromSuperview];
if (_complete) {
self.complete();
}
}