自定義Modal轉(zhuǎn)場-模仿push & pop

前言:

  • 昨天發(fā)了一篇文章 多層present,dismiss回到首次present的控制器 中提到自定義轉(zhuǎn)場動畫,實(shí)現(xiàn)從一個(gè)透明的導(dǎo)航控制器 跳轉(zhuǎn)到 非透明的導(dǎo)航控制器,其中第一種方法就是通過自定義modal 轉(zhuǎn)場,模仿push & pop 實(shí)現(xiàn),本文章中將會詳細(xì)說明

  • 先上效果圖: gitHub地址 喜歡就給個(gè)star 唄~??

1.gif

自定義轉(zhuǎn)場需要用到什么東西?

  • 1.UIViewControllerAnimatedTransitioning 一個(gè)協(xié)議,這個(gè)接口負(fù)責(zé)切換的具體內(nèi)容,也即“切換中應(yīng)該發(fā)生什么”,動畫實(shí)現(xiàn)只需要實(shí)現(xiàn)其兩個(gè)代理方法就行

  • 2.UIViewControllerTransitioningDelegate 一個(gè)協(xié)議,需要VC切換的時(shí)候系統(tǒng)會向?qū)崿F(xiàn)了這個(gè)接口的對象詢問是否需要使用自定義的切換效果,也就是選擇自定義的動畫

  • 3.UIPresentationController 控制控制器跳轉(zhuǎn)的類,是 iOS8 新增的一個(gè) API,用來控制 controller 之間的跳轉(zhuǎn)特效,例如:顯示一個(gè)模態(tài)窗口,大小和位置是自定義的,遮罩在原來的頁面。參考

  • 4.modalPresentationStyle 這是UIViewController 的一個(gè)屬性,就是字面意思,modal的樣式,自定義的話,需要設(shè)置為Custom

  • 5.transitioningDelegate 就是誰去實(shí)現(xiàn) UIViewControllerTransitioningDelegate 這個(gè)協(xié)議的代理方法,一般用一個(gè)專門的類管理,接著看下去就知道啦~

一步一個(gè)腳印,代碼實(shí)現(xiàn)來了,具體分析代碼中有注釋~

  • 1.首先新建一個(gè)繼承NSObject 的動畫管理類FLTransitionAnimation,管理是執(zhí)行present 、dismiss 動畫 ,在m文件的延展中遵守UIViewControllerAnimatedTransitioning 協(xié)議并實(shí)現(xiàn)兩個(gè)代理方法
.h文件:
* * * * * * * * * * * * * * * * * * * * * * * *
#import <Foundation/Foundation.h>

// present or dismiss
typedef enum{
    FLTransitionAnimationTypePresent,
    FLTransitionAnimationTypeDismiss
}FLTransitionAnimationType;

// present direction
typedef enum{
    FLPresentDirectionTypeFromLeft,
    FLPresentDirectionTypeFromRight
}FLPresentDirectionType;

// present direction
typedef enum{
    FLDismissDirectionTypeFromLeft,
    FLDismissDirectionTypeFromRight
}FLDismissDirectionType;

@interface FLTransitionAnimation : NSObject

/**
 *  @author 孔凡列, 16-09-02 02:09:13
 *
 *  present or dismiss
 */
@property (nonatomic,assign)FLTransitionAnimationType fl_transitionAnimationType;
/**
 *  @author 孔凡列, 16-09-02 04:09:56
 *
 *  present direction
 */
@property (nonatomic,assign)FLPresentDirectionType fl_presentDirectionType;
/**
 *  @author 孔凡列, 16-09-02 04:09:05
 *
 *  dismiss direction
 */
@property (nonatomic,assign)FLDismissDirectionType fl_dismissDirectionType;

@end
.m文件:
* * * * * * * * * * * * * * * * * * * * * * * *
#import "FLTransitionAnimation.h"
@import UIKit;

@interface FLTransitionAnimation () <UIViewControllerAnimatedTransitioning>

@end

@implementation FLTransitionAnimation
// 動畫執(zhí)行的時(shí)間
static const CGFloat duration = 0.3;
/**
 *  @author 孔凡列, 16-09-02 04:09:30
 *
 *  返回動畫執(zhí)行的時(shí)間
 *
 *  @param transitionContext 實(shí)現(xiàn)動畫效果時(shí)可以從參數(shù)transitionContext中獲取到切換時(shí)的上下文信息,比方說從哪個(gè)VC切換到哪個(gè)VC等
 *
 *  @return return value description
 */
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext{
    return duration;
}

/**
 *  @author 孔凡列, 16-09-02 04:09:25
 *
 *  執(zhí)行具體動畫
 *
 *  @param transitionContext 上下文信息
 */
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext{
    
    if (self.fl_transitionAnimationType == FLTransitionAnimationTypePresent) {
        // 1.get toView
        UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
        // 2.change frame
        __block CGRect tempFrame = toView.frame;
        if (self.fl_presentDirectionType == FLPresentDirectionTypeFromRight) {
            tempFrame.origin.x = toView.frame.size.width;
        }
        else if (self.fl_presentDirectionType == FLPresentDirectionTypeFromLeft) {
            tempFrame.origin.x = -toView.frame.size.width;
        }
        toView.frame = tempFrame;
        // 3.begin animation
        [UIView animateWithDuration:duration animations:^{
            tempFrame.origin.x = 0;
            toView.frame = tempFrame;
        } completion:^(BOOL finished) {
            // 4.Tell context that we completed
            [transitionContext completeTransition:YES];
        }];
    }
    else if (self.fl_transitionAnimationType == FLTransitionAnimationTypeDismiss){
        // 1.get toView
        UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
        // 2.change frame
        __block CGRect tempFrame = fromView.frame;
        // 3.begin animation
        [UIView animateWithDuration:duration animations:^{
            if (self.fl_dismissDirectionType == FLDismissDirectionTypeFromRight) {
                tempFrame.origin.x = -fromView.frame.size.width;
            }
            else if (self.fl_dismissDirectionType == FLDismissDirectionTypeFromLeft) {
                tempFrame.origin.x = fromView.frame.size.width;
            }
            fromView.frame = tempFrame;
        } completion:^(BOOL finished) {
            // 4.Tell context that we completed
            [transitionContext completeTransition:YES];
        }];
    }
}

@end
  • 2.上文中說到,實(shí)現(xiàn)UIViewControllerTransitioningDelegate 的代理方法一般用一個(gè)類來實(shí)現(xiàn)并管理,F(xiàn)LTransitionManager 就是專門來實(shí)現(xiàn)這個(gè)協(xié)議的代理方法的,比如present 或者 dismiss 是以什么動畫進(jìn)行的,在m文件中遵循 UIViewControllerTransitioningDelegate 協(xié)議 并實(shí)現(xiàn)其代理方法,告訴它present 和 dismiss 需要哪個(gè)動畫實(shí)例
.h 文件
* * * * * * * * * * * * * * * * * * * * * * * *
#import <Foundation/Foundation.h>
@import UIKit;
// present direction
typedef enum{
    FLPresentTypeFromLeft,
    FLPresentTypeFromRight
}FLPresentType;

// present direction
typedef enum{
    FLDismissTypeFromLeft,
    FLDismissTypeFromRight
}FLDismissType;

@interface FLTransitionManager : NSObject<UIViewControllerTransitioningDelegate>

/**
 *  @author 孔凡列, 16-09-02 04:09:56
 *
 *  present direction
 */
@property (nonatomic,assign)FLPresentType fl_presentType;
/**
 *  @author 孔凡列, 16-09-02 04:09:05
 *
 *  dismiss direction
 */
@property (nonatomic,assign)FLDismissType fl_dismissType;

// 創(chuàng)建一個(gè)單例實(shí)例

+ (instancetype)shareManager;

@end
.m 文件
* * * * * * * * * * * * * * * * * * * * * * * *
#import "FLTransitionManager.h"
// 具體動畫實(shí)例
#import "FLTransitionAnimation.h"

@interface FLTransitionManager ()

@end

@implementation FLTransitionManager

static FLTransitionManager *_instance = nil;
+ (instancetype)shareManager{
    if (!_instance) {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _instance = [[self alloc] init];
            // 設(shè)置默認(rèn)值
            _instance.fl_presentType = FLPresentTypeFromRight;
            _instance.fl_dismissType = FLDismissTypeFromLeft;
        });
    }
    return _instance;
}

/**
 *  @author 孔凡列, 16-09-02 05:09:10
 *
 *  present 調(diào)用
 *
 *  @param presented  被 present 的控制器
 *  @param presenting 正在 present 的控制器
 *  @param source     The view controller whose presentViewController:animated:completion: method was called. 就是調(diào)present那個(gè)控制器
 *
 *  @return 返回一個(gè)動畫實(shí)例
 */
- (id )animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source{
    FLTransitionAnimation *presentA = [[FLTransitionAnimation alloc] init];
    presentA.fl_transitionAnimationType = FLTransitionAnimationTypePresent;
    if (self.fl_presentType == FLPresentTypeFromLeft) {
        presentA.fl_presentDirectionType = FLPresentDirectionTypeFromLeft;
    }
    else{
        presentA.fl_presentDirectionType = FLPresentDirectionTypeFromRight;
    }
    return presentA;
}

/**
 *  @author 孔凡列, 16-09-02 05:09:20
 *
 *  dismiss 調(diào)用
 *
 *  @param dismissed 要dismiss的控制器
 *
 *  @return 返回一個(gè)動畫實(shí)例
 */
- (id )animationControllerForDismissedController:(UIViewController *)dismissed{
    FLTransitionAnimation *dismissA = [[FLTransitionAnimation alloc] init];
    dismissA.fl_transitionAnimationType = FLTransitionAnimationTypeDismiss;
    if (self.fl_dismissType == FLDismissTypeFromLeft) {
        dismissA.fl_dismissDirectionType = FLDismissDirectionTypeFromLeft;
    }
    else if (self.fl_dismissType == FLDismissTypeFromRight){
        dismissA.fl_dismissDirectionType = FLDismissDirectionTypeFromRight;
    }
    return dismissA;
}
@end
  • 3.此時(shí)設(shè)置 modalPresentationStyle 為 Custom 并且設(shè)置transitioningDelegate 為上面創(chuàng)建的管理類FLTransitionManager對象,然后調(diào)用present 方法 是沒任何反應(yīng)的,因?yàn)槲腋嬖V系統(tǒng),我要自定義這個(gè)present,那么系統(tǒng)就不幫你管理了(個(gè)人看法,如果我理解錯(cuò)了,望糾正~~??),那么我就需要給系統(tǒng)一個(gè)管理控制器之間跳轉(zhuǎn)以及特效 的一個(gè)控制器,iOS 8 之后,系統(tǒng)提供一個(gè)API 就是返回這么一個(gè)控制器的 ,這個(gè)是UIViewControllerTransitioningDelegate 的一個(gè)代理方法,返回一個(gè)UIPresentationController 實(shí)例,需要繼承自定義,然后告訴它要被present的view,這樣才能present喔 那么iOS7該怎么處理呢?傳送門
.h 文件
* * * * * * * * * * * * * * * * * * * * * * * *
#import <UIKit/UIKit.h>

@interface FLPresentationController : UIPresentationController

@end

* * * * * * * * * * * * * * * * * * * * * * * *
.m文件

#import "FLPresentationController.h"

@implementation FLPresentationController{
    // cover view
    UIView *coverView;
}
/**
 *  @author 孔凡列, 16-09-02 06:09:59
 *
 *  重寫構(gòu)造方法,添加效果
 *
 *  @param presentedViewController  被present 的控制器
 *  @param presentingViewController 正在present 的控制器
 *
 *  @return return value description
 */
- (instancetype)initWithPresentedViewController:(UIViewController *)presentedViewController presentingViewController:(UIViewController *)presentingViewController{
    if (self = [super initWithPresentedViewController:presentedViewController presentingViewController:presentingViewController]) {
        // 添加一些特效什么的~~~這里簡單添加了一個(gè)遮蓋的view,簡單做效果演示
        coverView = [[UIView alloc] init];
        coverView.backgroundColor = [UIColor redColor];
        coverView.alpha = 0.0;
    }
    return self;
}

/**
 *  @author 孔凡列, 16-09-02 05:09:36
 *
 *  準(zhǔn)備present
 */
- (void)presentationTransitionWillBegin{
    NSLog(@"present will begin");
   // 簡單添加效果
    coverView.frame = self.containerView.bounds;
    [self.containerView addSubview:coverView];
    
    // 最重要的 最重要的 最重要的 添加要present的view到容器里面,不然無法present,因?yàn)橄到y(tǒng)都不知道present什么,而且一定要最后添加
    [self.containerView addSubview:self.presentedView];
    
    id<UIViewControllerTransitionCoordinator> coordinator = self.presentingViewController.transitionCoordinator;
    
    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
        coverView.alpha = 1;
    } completion:nil];
}
/**
 *  @author 孔凡列, 16-09-02 06:09:12
 *
 *  present 結(jié)束
 *
 *  @param completed completed description
 */
- (void)presentationTransitionDidEnd:(BOOL)completed{
    NSLog(@"present did end");
    if(!completed){
        [coverView removeFromSuperview];
    }
}
/**
 *  @author 孔凡列, 16-09-02 06:09:15
 *
 *  準(zhǔn)備 dismiss
 */
- (void)dismissalTransitionWillBegin{
    
    NSLog(@"dismiss will begin");
    id<UIViewControllerTransitionCoordinator> coordinator = self.presentingViewController.transitionCoordinator;
    
    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
        coverView.alpha = 0.0;
    } completion:nil];
}
/**
 *  @author 孔凡列, 16-09-02 06:09:18
 *
 *  dismiss 結(jié)束
 *
 *  @param completed completed description
 */
- (void)dismissalTransitionDidEnd:(BOOL)completed{
    NSLog(@"dismiss did end");
    if(completed){
        [coverView removeFromSuperview];
    }
}
  • 4.此時(shí)在FLTransitionManager 的 m文件中實(shí)現(xiàn) presentationControllerForPresentedViewController 這個(gè)代理方法,返回自定義的FLPresentationController 就OK
/**
 *  @author 孔凡列, 16-09-02 05:09:19
 *
 *  返回一個(gè) 用來控制 controller 之間的跳轉(zhuǎn)特效 的控制器 (注意此API是 iOS8之后才有)
 *
 *  @param presented  被 present 的控制器
 *  @param presenting 正在 present 的控制器
 *  @param source     誰調(diào)present
 *
 *  @return 返回一個(gè) 用來控制 controller 之間的跳轉(zhuǎn)特效 的控制器
 */
- (nullable UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source NS_AVAILABLE_IOS(8_0){
    return [[FLPresentationController alloc] initWithPresentedViewController:presented presentingViewController:presenting];
}
  • 5.此時(shí)終于可以調(diào)用了,調(diào)用只需要比平時(shí)present多幾句代碼~~
    SecondViewController  *vc = [[SecondViewController alloc] init];
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
    
    nav.modalPresentationStyle = UIModalPresentationCustom;
    nav.transitioningDelegate = [FLTransitionManager shareManager];
    
    [self presentViewController:nav animated:YES completion:nil];


    dismiss 直接調(diào),什么都不用設(shè)置
    [self dismissViewControllerAnimated:YES completion:nil];
  • 6.最后附上效果圖~~~哈哈,辛苦啦!
2.gif

總結(jié)來了!

  • 其實(shí)沒啥好說的,展望一下未來吧~~
    • 上面實(shí)現(xiàn)的是動畫式切換,還有一種是交互式切換,給你一張圖看看
iOS 7中視圖控制器切換.png

恩,還不夠清楚!參考這個(gè)-->iOS7之定制View Controller切換效果

系統(tǒng)的pop 就是交互式切換的,因此,上面的還不算高仿,入棧這個(gè)不搞了,往后就先弄個(gè)拖拽手勢~期待ing

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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