前言:
昨天發(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 唄~??
自定義轉(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.最后附上效果圖~~~哈哈,辛苦啦!
總結(jié)來了!
- 其實(shí)沒啥好說的,展望一下未來吧~~
- 上面實(shí)現(xiàn)的是動畫式切換,還有一種是交互式切換,給你一張圖看看
恩,還不夠清楚!參考這個(gè)-->iOS7之定制View Controller切換效果
系統(tǒng)的pop 就是交互式切換的,因此,上面的還不算高仿,入棧這個(gè)不搞了,往后就先弄個(gè)拖拽手勢~期待ing