有關(guān)UINavigationController頁面之間的跳轉(zhuǎn)動(dòng)畫

最近遇到一個(gè)需求,一個(gè)導(dǎo)航控制器上有一個(gè)按鈕點(diǎn)擊彈出一個(gè)全屏幕的蒙層纲辽,然后蒙層上有個(gè)按鈕點(diǎn)擊從屏幕右邊push過來一個(gè)頁面祈纯;

先來張草圖感受下需求是什么(很直觀有木有):

頁面跳轉(zhuǎn).png

我們可以形象的舉個(gè)矾端,如果蒙層我們也算做一個(gè)控制器的話,有三個(gè)控制器携冤,oneVC悼粮,twoVC,threeVC曾棕,oneVC和threeVC是有導(dǎo)航欄的扣猫,twoVC(蒙層)是全屏幕的控制器,需求是點(diǎn)擊oneVC上的按鈕彈出twoVC(要求動(dòng)畫是彈出)翘地,點(diǎn)擊twoVC上的按鈕push過來threeVC(動(dòng)畫是一般的屏幕右邊push過來)

好了申尤,拿到這個(gè)需求,這不是很簡(jiǎn)單嗎衙耕?剛開始做的時(shí)候也沒多想昧穿,項(xiàng)目里有一種蒙層控制器彈出的方法,是UIViewController的分類方法橙喘,直接通過oneVC控制器調(diào)用presentInWindow

- (void)presentInWindow
{
    self.view.alpha = 0;
    self.view.backgroundColor = [UIColor clearColor];
    [self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
    UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
    [keyWindow addSubview:self.view];
    [self retain];
    
    NSDictionary *dic = @{@"_view_": self.view};
    [keyWindow addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_view_]|"
                                                                      options:0
                                                                      metrics:0
                                                                        views:dic]];
    [keyWindow addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_view_]|"
                                                                      options:0
                                                                      metrics:0
                                                                        views:dic]];
    
    __block typeof(self) weakSelf = self;
    
    [UIView animateWithDuration:0.3 animations:^{
        weakSelf.view.alpha = 1.0f;
    }];
}

- (void)dismiss
{
    __block typeof(self) weakSelf = self;
    
    [UIView animateWithDuration:0.3 animations:^{
        weakSelf.view.alpha = 0.0f;
    } completion:^(BOOL finished) {
        [weakSelf.view removeFromSuperview];
        [weakSelf release];
    }];
}

然后再點(diǎn)擊twoVC上的按鈕調(diào)用系統(tǒng)的push方法

[self.navigationController pushViewController:threeVC animated:YES];

好了 點(diǎn)擊完根本沒有任何反應(yīng)时鸵,而且斷點(diǎn)直接走到了threeVC的 -(void)dealloc方法,突然懵逼了厅瞎,這才發(fā)現(xiàn)調(diào)用presentInWindow出來的twoVC并不是導(dǎo)航控制器饰潜,再調(diào)用push方法,所以跳轉(zhuǎn)不過去和簸;看到有人實(shí)現(xiàn)的一種方案彭雾,他是這樣實(shí)現(xiàn)的:
首先調(diào)用presentInWindow彈出twoVC,然后點(diǎn)擊twoVC上的按鈕時(shí)锁保,調(diào)用了 - (void)dismiss方法關(guān)閉twoVC薯酝,通過回調(diào)的方法從oneVC push到threeVC半沽,但是這個(gè)有個(gè)問題就是返回的時(shí)候twoVC不見了,可能有人想問吴菠,我通過回調(diào)從 oneVC 跳轉(zhuǎn)到threeVC的時(shí)候不關(guān)閉twoVC不就保證三個(gè)頁面都在了么者填,再仔細(xì)看看才發(fā)現(xiàn)presentInWindow是把twoVC加到了UIWindow上,這樣threeVC push過來的時(shí)候?qū)蛹?jí)就在twoVC下面橄务,就被蓋著了幔托。

經(jīng)過嘗試穴亏,找到了三種解決方案蜂挪,分享給大家,實(shí)現(xiàn)方式都很簡(jiǎn)單嗓化,都是把三個(gè)VC作為導(dǎo)航控制器來跳轉(zhuǎn)棠涮。

方案一

這種方式借助上面的presentInWindow方法,直接把twoVC包裝成UINavigationController刺覆,然后去調(diào)用presentInWindow严肪,代碼如下:

    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:twoVC];
    [nav presentInWindow];

關(guān)閉twoVC調(diào)用

    [self.navigationController dismiss];

然后再跳轉(zhuǎn)threeVC

    [self.navigationController pushViewController:threeVC animated:NO];

方案二

這種方式是利用系統(tǒng)的CATransition動(dòng)畫來設(shè)置彈出效果,廢話不多說谦屑,上代碼:

    CATransition *animation = [CATransition animation];
    [animation setDuration:0.3];
    [animation setType: kCATransitionFade];
    [animation setSubtype: kCATransitionFromTop];
    [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]];
    [self.navigationController pushViewController:twoVC animated:NO];
    [self.navigationController.view.layer addAnimation:animation forKey:nil];

關(guān)閉當(dāng)前頁面直接調(diào)用

      [self.navigationController popViewControllerAnimated:NO];

這種方式其實(shí)就是正常的push驳糯,只是給push添加了一個(gè)動(dòng)畫,也比較簡(jiǎn)單

方案三

這種方式相比前兩種最為復(fù)雜氢橙,因?yàn)榭梢宰远x酝枢,是iOS7新增的;
蘋果給我們開發(fā)者提供的是都是協(xié)議接口,所以我們能夠很好的單獨(dú)提出來寫成一個(gè)個(gè)類,在里面實(shí)現(xiàn)我們各種自定義效果
1.先來看看實(shí)現(xiàn)UIViewControllerAnimatedTransitioning的自定義動(dòng)畫類
我們定義了一個(gè)實(shí)體類CustomAnimator:

/**
 *  自定義的動(dòng)畫類
 *  實(shí)現(xiàn)協(xié)議------>@protocol UIViewControllerAnimatedTransitioning
 *  這個(gè)接口負(fù)責(zé)切換的具體內(nèi)容悍手,也即“切換中應(yīng)該發(fā)生什么”
 */
//CustomAnimator.h
#import <Foundation/Foundation.h>
typedef enum {
    AnimationTypePresent,
    AnimationTypeDismiss
} AnimationType;

@interface CustomAnimator : NSObject<UIViewControllerAnimatedTransitioning>
@property (nonatomic, assign) AnimationType type;
@end

//CustomAnimator.m
@implementation CustomAnimator

// 系統(tǒng)給出一個(gè)切換上下文帘睦,我們根據(jù)上下文環(huán)境返回這個(gè)切換所需要的花費(fèi)時(shí)間
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
    return 1.0;
}

// 完成容器轉(zhuǎn)場(chǎng)動(dòng)畫的主要方法,我們對(duì)于切換時(shí)的UIView的設(shè)置和動(dòng)畫都在這個(gè)方法中完成
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    // 可以看做為destination ViewController
    UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    // 可以看做為source ViewController
    UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    if (self.type == AnimationTypePresent) {
        //Add 'to' view to the hierarchy with 0.0 scale
        toViewController.view.transform = CGAffineTransformMakeScale(0.95, 0.95);
        toViewController.view.alpha = 0;
        [containerView insertSubview:toViewController.view aboveSubview:fromViewController.view];
        
        //iOS9 兼容   
        [toViewController.view mas_makeConstraints:^(MASConstraintMaker *make) {
            make.edges.mas_equalTo(UIEdgeInsetsZero);
        }];
        
        [UIView animateKeyframesWithDuration:[self transitionDuration:transitionContext] delay:0.0 options:0 animations:^{
            [UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:0.15 animations:^{
                toViewController.view.transform = CGAffineTransformMakeScale(1.0, 1.0);
            }];
            [UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:[self transitionDuration:transitionContext] animations:^{
                toViewController.view.alpha = 1;
            }];
        } completion:^(BOOL finished) {
            [transitionContext completeTransition:YES];
        }];
    } else if (self.type == AnimationTypeDismiss) {
        //Add 'to' view to the hierarchy
        [containerView insertSubview:toViewController.view belowSubview:fromViewController.view];
        [UIView animateKeyframesWithDuration:[self transitionDuration:transitionContext] delay:0.0 options:0 animations:^{
            [UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:[self transitionDuration:transitionContext] animations:^{
                fromViewController.view.alpha = 0;
            }];
            [UIView addKeyframeWithRelativeStartTime:0.15 relativeDuration:0.15 animations:^{
                fromViewController.view.transform = CGAffineTransformMakeScale(0.95, 0.95);
            }];
        } completion:^(BOOL finished) {
            [transitionContext completeTransition:YES];
        }];
    }
}

PS:從協(xié)議中兩個(gè)方法可以看出坦康,上面兩個(gè)必須實(shí)現(xiàn)的方法需要一個(gè)轉(zhuǎn)場(chǎng)上下文參數(shù)竣付,這是一個(gè)遵從UIViewControllerContextTransitioning 協(xié)議的對(duì)象。通常情況下滞欠,當(dāng)我們使用系統(tǒng)的類時(shí)古胆,系統(tǒng)框架為我們提供的轉(zhuǎn)場(chǎng)代理(Transitioning Delegates),為我們創(chuàng)建了轉(zhuǎn)場(chǎng)上下文對(duì)象,并把它傳遞給動(dòng)畫控制器筛璧。

然后在oneVC中需要實(shí)現(xiàn)UINavigationControllerDelegate

    //**不要忘了設(shè)置代理**
    self.navigationController.delegate = self;

跳轉(zhuǎn)還是正常的push

    [self.navigationController pushViewController:twoVC animated:YES];

然后在oneVC中實(shí)現(xiàn)以下代理方法:

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {
   //這里需要判斷下fromVC和toVC
    CustomAnimator *animationController = [[CustomAnimator alloc] init];
    switch (operation) {
        case UINavigationControllerOperationPush:
            animationController.type = AnimationTypePresent;
            return  animationController;
        case UINavigationControllerOperationPop:
            animationController.type = AnimationTypeDismiss;
            return animationController;
        default: return nil;
    }
}

有個(gè)問題需要注意下逸绎,修改了動(dòng)畫之后你需要指定下是哪個(gè)控制器需要這種彈出效果,在上面的代理方法里面注釋處隧哮,如果不指定就默人修改了所有頁面的push動(dòng)畫桶良,這種自定義效果還是比較好的,但是實(shí)現(xiàn)起來也比前兩種更為復(fù)雜沮翔,還是一句話陨帆,看需求來選擇哪種方案實(shí)現(xiàn)曲秉。

另外:在twoVC中別忘了實(shí)現(xiàn)導(dǎo)航欄的隱藏:

- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    
    [self.navigationController setNavigationBarHidden:YES animated:animated];
}

再在threeVC中實(shí)現(xiàn)導(dǎo)航欄的顯示:

- (void)viewWillAppear:(BOOL)animated {
    [self.navigationController setNavigationBarHidden:NO animated:animated];
}

好了,三種方案都已經(jīng)介紹完了疲牵,希望可以對(duì)大家有所幫助承二。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市纲爸,隨后出現(xiàn)的幾起案子亥鸠,更是在濱河造成了極大的恐慌,老刑警劉巖识啦,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件负蚊,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡颓哮,警方通過查閱死者的電腦和手機(jī)家妆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來冕茅,“玉大人伤极,你說我怎么就攤上這事∫躺耍” “怎么了哨坪?”我有些...
    開封第一講書人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長乍楚。 經(jīng)常有香客問我当编,道長,這世上最難降的妖魔是什么炊豪? 我笑而不...
    開封第一講書人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任凌箕,我火速辦了婚禮,結(jié)果婚禮上词渤,老公的妹妹穿的比我還像新娘牵舱。我一直安慰自己,他們只是感情好缺虐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開白布芜壁。 她就那樣靜靜地躺著,像睡著了一般高氮。 火紅的嫁衣襯著肌膚如雪慧妄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評(píng)論 1 307
  • 那天剪芍,我揣著相機(jī)與錄音塞淹,去河邊找鬼。 笑死罪裹,一個(gè)胖子當(dāng)著我的面吹牛饱普,可吹牛的內(nèi)容都是我干的运挫。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼套耕,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼谁帕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起冯袍,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤匈挖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后康愤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體儡循,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年翘瓮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了贮折。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片裤翩。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡资盅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出踊赠,到底是詐尸還是另有隱情呵扛,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布筐带,位于F島的核電站今穿,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏伦籍。R本人自食惡果不足惜蓝晒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望帖鸦。 院中可真熱鬧芝薇,春花似錦、人聲如沸作儿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽攻锰。三九已至晾嘶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間娶吞,已是汗流浹背垒迂。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留妒蛇,地道東北人机断。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓策添,卻偏偏與公主長得像,于是被迫代替她去往敵國和親毫缆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子唯竹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

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

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件苦丁、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,109評(píng)論 4 62
  • 翻譯自“View Controller Programming Guide for iOS”浸颓。 1 彈出視圖控制器...
    lakerszhy閱讀 3,528評(píng)論 2 20
  • 【論語心得039】如有神在。祭如在旺拉,祭神如神在产上。祭祖先就好像祖先真的在場(chǎng)一樣,拜神仙蛾狗,就當(dāng)神仙就在那一樣晋涣。這是不是...
    國學(xué)應(yīng)用講習(xí)所閱讀 183評(píng)論 0 0
  • 你是否注意 天空是粉紅色的 每當(dāng)你抬頭看時(shí) 它都會(huì)冷漠的變成藍(lán)色 掩飾住自己心跳的顏色 其實(shí)它是害羞的粉色 在你不...
    Zing13閱讀 264評(píng)論 0 1