iOS -自定義transitionContext轉(zhuǎn)場動(dòng)畫(高仿QQ電話政溃、折疊動(dòng)畫)

整體結(jié)構(gòu)##

封裝一個(gè)基本轉(zhuǎn)場動(dòng)畫的公共父類LZBBaseTransition.h,用于實(shí)現(xiàn)一些基本共同的操作 以及封裝block回調(diào),子類繼承LZBBaseTransition,重寫- (void)animateTransition:(id)transitionContext實(shí)現(xiàn)動(dòng)畫效果

LZBPageTransition.h 翻頁效果
LZBBubbleTransition.h 氣泡效果
LZBQQPhoneTransition.h 模擬QQ電話
LZBCustomModalTransition.h 自定義模態(tài)動(dòng)畫
LZBPresentDismissTransition.h 模擬系統(tǒng)的模態(tài)動(dòng)畫
LZBPushPopTransition.h 模擬系統(tǒng)的導(dǎo)航切換動(dòng)畫

這些可以直接分塊使用留储,耦合性不高翼抠,代碼包也不大
下載地址:各種轉(zhuǎn)場動(dòng)畫github下載鏈接

自定義轉(zhuǎn)場動(dòng)畫.gif
自定義轉(zhuǎn)場動(dòng)畫.gif

基本原理分析##

我們都應(yīng)該了解一個(gè)基本的知識(shí)是為什么我們的push/pop以及模態(tài)動(dòng)畫能實(shí)現(xiàn)系統(tǒng)的轉(zhuǎn)場視圖切換?
原因:與轉(zhuǎn)場代理有關(guān)获讳。
模態(tài)切換視圖與控制器的transitioningDelegate有關(guān)
導(dǎo)航切換與navigationController.delegate 有光
所以我們可以改變代理對(duì)象阴颖,并且重寫轉(zhuǎn)場的協(xié)議方法就可以實(shí)現(xiàn)自定義轉(zhuǎn)場動(dòng)畫

以下這三個(gè)協(xié)議很重要,我是重寫協(xié)議方法丐膝,轉(zhuǎn)換為block回調(diào)(個(gè)人習(xí)慣而已)

UIViewControllerAnimatedTransitioning //轉(zhuǎn)場動(dòng)畫協(xié)議
UIViewControllerTransitioningDelegate //轉(zhuǎn)場代理協(xié)議
UINavigationControllerDelegate //導(dǎo)航代理協(xié)議

一般的實(shí)現(xiàn)步驟####

第一步:重寫方法

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;

第二步:通過轉(zhuǎn)場上下文拿到容器containerView

UIView *containerView = [transitionContext containerView];

第三步:拿到源控制器的View(A ->B,A是源控制器)量愧,拿到目的控制器(B是目的控制器)的View,并增加到容器containerView中,注意順序(一定是目的控制器的View在頂層)帅矗,并都要設(shè)置frame

  - (UIView *)fromView:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIView *fromView = nil;
//源控制器
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
if([transitionContext respondsToSelector:@selector(viewForKey:)])
{
    fromView =  [transitionContext viewForKey:UITransitionContextFromViewKey];
}
else
    fromView = fromVC.view;
//初始位置的frame
fromView.frame = [transitionContext initialFrameForViewController:fromVC];
return fromView;
}

第四步:設(shè)置轉(zhuǎn)場動(dòng)畫

功能實(shí)現(xiàn)詳解析####

功能之高仿QQ電話切換效果#####

方法使用

twoQQPhoneViewController *twoVC = [[twoQQPhoneViewController alloc]init];
twoVC.modalPresentationStyle =  UIModalPresentationCustom;
__weak typeof(self) weakSelf = self;
self.QQPhoneTransition = [[LZBQQPhoneTransition alloc]initWithPresent:^(UIViewController *presented, UIViewController *presenting, UIViewController *sourceVC, LZBBaseTransition *transition) {
    LZBQQPhoneTransition *modalQQ = (LZBQQPhoneTransition*)transition;
    modalQQ.targetView = weakSelf.presentButton;  
} Dismiss:^(UIViewController *dismissVC, LZBBaseTransition *transition) {   
}];
twoVC.transitioningDelegate = self.QQPhoneTransition;
[self presentViewController:twoVC animated:YES completion:nil];

由此可以見偎肃,自定義轉(zhuǎn)場動(dòng)畫,只是改變了控制器的轉(zhuǎn)場代理浑此,其實(shí)我只是把操作都封裝在了LZBQQPhoneTransition里面累颂,所以看起來簡單。現(xiàn)在就來看看LZBQQPhoneTransition這里面都在干什么凛俱。

第一步:LZBQQPhoneTransition 設(shè)置參數(shù)的初始化值

-(instancetype)initWithPresent:(LZBBaseTransitionPresent)presentCallBack Dismiss:(LZBBaseTransitionDismiss)dismissCallBack
 {
  if(self = [super initWithPresent:presentCallBack Dismiss:dismissCallBack])
 {
    self.scale = 3.0;
 }
return self;
}

第二步:調(diào)用轉(zhuǎn)場動(dòng)畫的代理方法

 - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
 {
UIView *containerView = [transitionContext containerView];
if(containerView == nil) return;
self.transitionContext = transitionContext;
if(self.transitionType == kLZBBaseTransitionStyle_Present)
{
    [self animationPresentTrasition:transitionContext WithContainerView:containerView];
}
else
{
    [self animationDismissTrasition:transitionContext WithContainerView:containerView];
}
}

第三步:核心是過渡的動(dòng)畫紊馏,所以第三步才是關(guān)鍵哦
present 過程是動(dòng)畫組 + 形狀層的CABasicAnimation做成的,對(duì)于想了解動(dòng)畫的朋友有幫助哦

首先是畫動(dòng)畫路徑

  //畫移動(dòng)曲線
CGPoint startPoint = self.targetView.center;
CGPoint endPoint = toView.center;
CGPoint controlPoint = CGPointMake(self.targetView.center.x, [UIScreen mainScreen].bounds.size.height * 0.5);
UIBezierPath *animationPath = [[UIBezierPath alloc]init];
[animationPath moveToPoint:startPoint];
[animationPath addQuadCurveToPoint:endPoint controlPoint:controlPoint];

然后在移動(dòng)過程中還需要放大朱监,所以有增加transform動(dòng)畫

  //增加動(dòng)畫
CAAnimationGroup *group = [self groupAnimationWithBezierPath:animationPath durationTime:1.0 transform:CATransform3DMakeScale(self.scale, self.scale, 1)];
group.removedOnCompletion = NO;
group.fillMode = kCAFillModeForwards;
//用于后面找到這組動(dòng)畫
[group setValue:@"onePresentGroup" forKey:@"groupAnimation"];
[self.targetView.layer addAnimation:group forKey:@"keyAniamition"];

第一個(gè)動(dòng)畫組完成之后暖哨,才是過渡切換視圖動(dòng)畫(監(jiān)聽使用動(dòng)畫的代理方法-animationDidStop:(CAAnimation *)anim finished:(BOOL)flag)完成轉(zhuǎn)場動(dòng)畫

轉(zhuǎn)場動(dòng)畫

  //用曲線畫兩個(gè)圓 -開始圓 + 結(jié)束圓
   //求出半徑
   CGFloat radius = sqrtf(containerView.frame.size.height *containerView.frame.size.height + containerView.frame.size.width *containerView.frame.size.width)*0.5;
   //根據(jù)半徑畫 - 結(jié)束圓
   UIBezierPath *endCircle = [UIBezierPath bezierPathWithArcCenter:containerView.center radius:radius startAngle:0 endAngle:2*M_PI clockwise:YES];
//QQPhone動(dòng)畫開始圓
   UIBezierPath *startCicle = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(([UIScreen mainScreen].bounds.size.width - self.targetView.frame.size.width * self.scale)*0.5, ([UIScreen mainScreen].bounds.size.height  - self.targetView.frame.size.height * self.scale)*0.5 , self.targetView.frame.size.width*self.scale, self.targetView.frame.size.height*self.scale)];
   //創(chuàng)建動(dòng)畫的形狀層
   CAShapeLayer *maskLayer = [CAShapeLayer layer];
   maskLayer.path = endCircle.CGPath;
   toVC.view.layer.mask = maskLayer;
   //創(chuàng)建過度路徑動(dòng)畫
   CABasicAnimation *laryerAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
   laryerAnimation.fromValue =(__bridge id )startCicle.CGPath;
   laryerAnimation.toValue =(__bridge id )endCircle.CGPath;
   laryerAnimation.duration = 1.0;
   laryerAnimation.delegate = self;
   [maskLayer addAnimation:laryerAnimation forKey:@"path"];
   self.targetView.hidden = YES;
   toVC.view.alpha = 1.0;

以上只是實(shí)現(xiàn)的核心思想,具體實(shí)現(xiàn)可以看看源代碼下載地址各種轉(zhuǎn)場動(dòng)畫github下載鏈接

最后贈(zèng)言###

如果覺得文章對(duì)您有幫助篇裁,不要忘記star哦!??,star 是對(duì)程序猿最大的鼓勵(lì)达布!

參考鏈接:標(biāo)哥技術(shù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市黍聂,隨后出現(xiàn)的幾起案子身腻,更是在濱河造成了極大的恐慌,老刑警劉巖嘀趟,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異她按,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)酌泰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來陵刹,“玉大人,你說我怎么就攤上這事欢嘿∷ニ觯” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵际插,是天一觀的道長碘耳。 經(jīng)常有香客問我,道長框弛,這世上最難降的妖魔是什么辛辨? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮瑟枫,結(jié)果婚禮上斗搞,老公的妹妹穿的比我還像新娘。我一直安慰自己慷妙,他們只是感情好僻焚,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著膝擂,像睡著了一般虑啤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上架馋,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天狞山,我揣著相機(jī)與錄音,去河邊找鬼叉寂。 笑死萍启,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播勘纯,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼局服,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了驳遵?” 一聲冷哼從身側(cè)響起淫奔,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎堤结,沒想到半個(gè)月后搏讶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡霍殴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年季惩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了看靠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡丧肴,死狀恐怖穿挨,靈堂內(nèi)的尸體忽然破棺而出月弛,到底是詐尸還是另有隱情,我是刑警寧澤科盛,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站贞绵,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏谴垫。R本人自食惡果不足惜母蛛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一彩郊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧恕出,春花似錦筷登、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抒线。三九已至渣慕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間逊桦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國打工睡陪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留兰迫,地道東北人炬称。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓玲躯,卻偏偏與公主長得像,于是被迫代替她去往敵國和親晋控。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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