iOS自定義轉(zhuǎn)場動畫(push梯轻、pop動畫)

iOS7推出了新的轉(zhuǎn)場動畫API,以協(xié)議id<UIViewControllerInterativeTransition>尽棕、id<UIViewAnimatedTransitioning>方式開放給開發(fā)者喳挑,不同于代理、類別滔悉,這樣更易于我們自定義動畫伊诵,更加靈活。下面介紹一下自定義轉(zhuǎn)場動畫

仿酷狗push
push
要使用的協(xié)議
  • UIViewControllerInteractiveTransitioning 交互協(xié)議回官,主要在右滑返回時用到
  • UIViewControllerAnimatedTransitioning 動畫協(xié)議曹宴,含有動畫時間及轉(zhuǎn)場上下文兩個必須實現(xiàn)協(xié)議
  • UIViewControllerContextTransitioning 動畫協(xié)議里邊的協(xié)議之一,動畫實現(xiàn)的主要部分
  • UIPrecentDrivenInteractiveTransition 用在交互協(xié)議歉提,百分比控制當前動畫進度笛坦。
自定義步驟
  • 首先要實現(xiàn)navigation的代理,navigation有兩個返回id類型的協(xié)議苔巨,實現(xiàn)這兩個協(xié)議

第一個方法返回一個UIPercentDrivenInterativeTransition類型的對象即可版扩,這個對象默認實現(xiàn)了UIPercentInterativeTransitioning協(xié)議,需要注意的是侄泽,這個返回值主要是用于交互動畫礁芦,也就是右滑返回時需要用到,這里我在基類baseViewController定義了一個UIPercentDrivenInterativeTransition類型的屬性。
- (nullable id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController )navigationController
interactionControllerForAnimationController:(WTKBaseAnimation
) animationControlle{
return animationControlle.interactivePopTransition;
}
第二個方法我自定義了一個遵循UIViewControllerAnimationTransitioning協(xié)議的類WTKBaseAnimation,
- (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(WTKBaseViewController *)fromVC
toViewController:(UIViewController *)toVC{
if (fromVC.interactivePopTransition)
{
WTKBaseAnimation *animation = [[WTKBaseAnimation alloc]initWithType:operation Duration:0.6 animateType:self.animationType];
animation.interactivePopTransition = fromVC.interactivePopTransition;
return animation; //手勢
}
else
{
WTKBaseAnimation *animation = [[WTKBaseAnimation alloc]initWithType:operation Duration:0.6 animateType:self.animationType];
return animation;//非手勢
};}

第二個方法返回對象自定義如下


baseAnimation構(gòu)建方法.png

也就是需要把navigation的代理方法中的參數(shù)都傳過來柿扣。
在本類中肖方,需要實現(xiàn)轉(zhuǎn)場動畫協(xié)議:
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext{
return self.duration;}

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext{
if (self.transitionType == UINavigationControllerOperationPush)
{
    [self push:transitionContext];
}
else if (self.transitionType == UINavigationControllerOperationPop)
{
    [self pop:transitionContext];
}}   

[self push:transitionContext]; [self pop:transitionContext];在本類中并沒有真正的實現(xiàn),具體交給子類實現(xiàn)

Paste_Image.png

- (void)push:(id<UIViewControllerContextTransitioning>)transitionContext{}
- (void)pop:(id<UIViewControllerContextTransitioning>)transitionContext{}

下面介紹子類的具體實現(xiàn)未状,

push

|
<pre><code>- (void)push:(id<UIViewControllerContextTransitioning>)transitionContext {

UIViewController * fromVc   = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];

UIViewController * toVc     = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

NSTimeInterval duration     = [self transitionDuration:transitionContext];

CGRect bounds               = [[UIScreen mainScreen] bounds];

fromVc.view.hidden          = YES;

[[transitionContext containerView] addSubview:toVc.view];

[[toVc.navigationController.view superview] insertSubview:fromVc.snapshot belowSubview:toVc.navigationController.view];

toVc.navigationController.view.transform = 

CGAffineTransformMakeTranslation(CGRectGetWidth(bounds), 0);

[UIView animateWithDuration:duration
                      delay:0
     usingSpringWithDamping:1.0
      initialSpringVelocity:0
                    options:UIViewAnimationOptionCurveLinear
                 animations:^{
                     fromVc.snapshot.transform = CGAffineTransformMakeTranslation(-CGRectGetWidth(bounds) * 0.3, 0);
                     toVc.navigationController.view.transform = CGAffineTransformMakeTranslation(0, 0);
                 }
                 completion:^(BOOL finished) {
                     fromVc.view.hidden = NO;
                     [fromVc.snapshot removeFromSuperview];
                     [transitionContext completeTransition:YES];
}];
}

</code></pre>
其中fromVC與toVC窥妇、為函數(shù)參數(shù)transitionContext協(xié)議獲得,duration調(diào)用父類方法獲得娩践,最終為navigation的代理方法中返回的時間活翩,也可以自定義。fromVC為原來的ViewController翻伺,toVC為要push的VC
首先將fromVC的view隱藏材泄,使用VC的snapshot代替,snapshot為viewController的截圖吨岭,這里使用類別關(guān)聯(lián)屬性實現(xiàn)的拉宗。
[transitionContext containerView] 為容器,存轉(zhuǎn)場需要的view辣辫,
分別將toVC.view及fromVC.snapshot添加到容器中旦事,注意添加順序、view存放的順序急灭。

下面將toVC移動到屏幕右邊姐浮,這里使用的是改變transform,

使用UIView做動畫葬馋,需要注意的是卖鲤,使用usingSpringWithDamping的動畫,關(guān)于這個動畫不再多說畴嘶。
UIView動畫蛋逾,需要把fromVC移動到左邊(移動多少可自定),toVC移動到右邊窗悯。
動畫完成后区匣,需要把原來隱藏的fromVC.view顯示,添加到容器的view移除蒋院,當前顯示的vc不需要移除亏钩。
** 最后需要調(diào)用轉(zhuǎn)場完成方法** [transitionContext completeTransition:YES];

Pop

|

  - (void)pop:(id<UIViewControllerContextTransitioning>)transitionContext {

WTKBaseViewController * fromVc  = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];

UIViewController * toVc         = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

NSTimeInterval duration         = [self transitionDuration:transitionContext];

CGRect bounds                   = [[UIScreen mainScreen] bounds];

[fromVc.view addSubview:fromVc.snapshot];
fromVc.navigationController.navigationBar.hidden = YES;
fromVc.view.transform = CGAffineTransformIdentity;

toVc.view.hidden                = YES;
toVc.snapshot.transform         = CGAffineTransformMakeTranslation(-CGRectGetWidth(bounds) * 0.3, 0);

[[transitionContext containerView] addSubview:toVc.view];
[[transitionContext containerView] addSubview:toVc.snapshot];
[[transitionContext containerView] sendSubviewToBack:toVc.snapshot];

if (fromVc.interactivePopTransition)
{
    [UIView animateWithDuration:duration
                          delay:0
                        options:UIViewAnimationOptionCurveLinear
                     animations:^{
                         fromVc.view.transform = CGAffineTransformMakeTranslation(CGRectGetWidth(bounds), 0.0);
                         toVc.snapshot.transform = CGAffineTransformIdentity;
                     }
                     completion:^(BOOL finished) {

                         toVc.navigationController.navigationBar.hidden = NO;
                         toVc.view.hidden = NO;

                         [fromVc.snapshot removeFromSuperview];
                         [toVc.snapshot removeFromSuperview];
                         fromVc.snapshot = nil;

                         if (![transitionContext transitionWasCancelled]) {
                             toVc.snapshot = nil;
                         }

                         [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
                     }];

}
else
{
    [UIView animateWithDuration:duration
                          delay:0
         usingSpringWithDamping:1
          initialSpringVelocity:0
                        options:UIViewAnimationOptionCurveLinear
                     animations:^{
                         fromVc.view.transform = CGAffineTransformMakeTranslation(CGRectGetWidth(bounds), 0.0);
                         toVc.snapshot.transform = CGAffineTransformIdentity;
                     }
                     completion:^(BOOL finished) {

                         toVc.navigationController.navigationBar.hidden = NO;
                         toVc.view.hidden = NO;

                         [fromVc.snapshot removeFromSuperview];
                         [toVc.snapshot removeFromSuperview];
                         fromVc.snapshot = nil;

                         if (![transitionContext transitionWasCancelled]) {
                             toVc.snapshot = nil;
                         }

                         [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
                     }];
}}

pop方法與push類似,不再多說悦污,需要注意的是** 一定要區(qū)分手勢和非手勢 **铸屉,也就是如果點擊按鈕返回钉蒲,需要使用usingSpringWithDamping動畫切端,右滑返回不使用這個。
判斷方式fromVc.interactivePopTransition顷啼,這個為在基類baseViewController里邊自定義的一個UIPercentDrivenInterativeTransition類型的屬性踏枣,也就是navigation代理方法- (nullable id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(WTKBaseAnimation*) animationControlle的返回值昌屉。

  • 在baseViewController中添加手勢:UIPanGestureRecognizer,添加到self.view上面,viewDidLoad中如下:

if (self.navigationController && self != self.navigationController.viewControllers.firstObject)
{
    UIPanGestureRecognizer *popRecognizer = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(handlePopRecognizer:)];
    [self.view addGestureRecognizer:popRecognizer];
    popRecognizer.delegate = self;
}

在手勢方法中創(chuàng)建UIPercentInterativeTransition茵瀑,在拖動過程中间驮,用這個實例變量調(diào)用updateInteractiveTransition方法,代碼如下


- (void)handlePopRecognizer:(UIPanGestureRecognizer *)recognizer{
CGFloat progress = [recognizer translationInView:self.view].x / CGRectGetWidth(self.view.frame);
progress = MIN(1.0, MAX(0.0, progress));
NSLog(@"progress---%.2f",progress);
if (recognizer.state == UIGestureRecognizerStateBegan)
{
    self.interactivePopTransition = [[UIPercentDrivenInteractiveTransition alloc]init];
    [self.navigationController popViewControllerAnimated:YES];
}
else if (recognizer.state == UIGestureRecognizerStateChanged)
{
    [self.interactivePopTransition updateInteractiveTransition:progress];
}
else if (recognizer.state == UIGestureRecognizerStateEnded || recognizer.state == UIGestureRecognizerStateCancelled)
{
    if (progress > 0.25)
    {
        [self.interactivePopTransition finishInteractiveTransition];
    }
    else
    {
        [self.interactivePopTransition cancelInteractiveTransition];
    }
    self.interactivePopTransition = nil;
}}

上面定義的progress马昨,為了記錄滑動的百分比竞帽,隨時更新interactivePopTransition 當手勢結(jié)束,根據(jù)progress判斷當前是否可以pop回來,這里是以0.25為標準鸿捧。

代碼連接 git連接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末屹篓,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子匙奴,更是在濱河造成了極大的恐慌堆巧,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泼菌,死亡現(xiàn)場離奇詭異谍肤,居然都是意外死亡,警方通過查閱死者的電腦和手機哗伯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門荒揣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人焊刹,你說我怎么就攤上這事乳附。” “怎么了伴澄?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵赋除,是天一觀的道長。 經(jīng)常有香客問我非凌,道長举农,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任敞嗡,我火速辦了婚禮颁糟,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘喉悴。我一直安慰自己棱貌,他們只是感情好,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布箕肃。 她就那樣靜靜地躺著婚脱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上障贸,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天错森,我揣著相機與錄音,去河邊找鬼篮洁。 笑死涩维,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的袁波。 我是一名探鬼主播瓦阐,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼篷牌!你這毒婦竟也來了垄分?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤娃磺,失蹤者是張志新(化名)和其女友劉穎薄湿,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體偷卧,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡豺瘤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了听诸。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坐求。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖晌梨,靈堂內(nèi)的尸體忽然破棺而出桥嗤,到底是詐尸還是另有隱情,我是刑警寧澤仔蝌,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布泛领,位于F島的核電站,受9級特大地震影響敛惊,放射性物質(zhì)發(fā)生泄漏渊鞋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一瞧挤、第九天 我趴在偏房一處隱蔽的房頂上張望锡宋。 院中可真熱鬧,春花似錦特恬、人聲如沸执俩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽役首。三九已至尝丐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間宋税,已是汗流浹背摊崭。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工讼油, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留杰赛,地道東北人。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓矮台,卻偏偏與公主長得像乏屯,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子瘦赫,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容