最近遇到一個(gè)需求,一個(gè)導(dǎo)航控制器上有一個(gè)按鈕點(diǎn)擊彈出一個(gè)全屏幕的蒙層纲辽,然后蒙層上有個(gè)按鈕點(diǎn)擊從屏幕右邊push過來一個(gè)頁面祈纯;
先來張草圖感受下需求是什么(很直觀有木有):
我們可以形象的舉個(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ì)大家有所幫助承二。