方案一:開啟使用系統自帶的側滑返回
iOS7之后系統提供了側滑手勢(interactivePopGestureRecognizer),即從屏幕左側邊緣滑起會pop回導航控制器棧的上個viewController。不過如果你自定義了返回按鈕,系統自帶的側滑返回功能會失效。此時需要添加下面的代碼解決:
self.navigationController.interactivePopGestureRecognizer.delegate = (id)self;
缺點:
必須從屏幕邊緣左側滑起才會觸發;
一旦自定義導航控制器或者自定義返回按鈕,就會失效。
方案二:極其簡單取巧的方法
iOS7之后是有側滑返回手勢功能的。注意,也就是說系統已經定義了一種手勢,并且給這個手勢已經添加了一個觸發方法(重點)。但是,系統的這個手勢的觸發條件是必須從屏幕左邊緣開始滑動。我們取巧的方法是自己寫一個支持全屏滑動的手勢,而其觸發方法系統已經有,沒必要自己實現pop的動畫,所以直接就把系統的觸發處理方法作為我們自己定義的手勢的處理方法。
#import "RootNavigationController.h"
@interface RootNavigationController ()<UIGestureRecognizerDelegate>
@end
@implementation RootNavigationController
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationBar.layoutMargins = UIEdgeInsetsZero;
// 全屏右滑,自己添加手勢,采用系統的代理,以及調用系統的方法;
//系統的私有API
SEL internalAction = NSSelectorFromString(@"handleNavigationTransition:");
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self.interactivePopGestureRecognizer.delegate action:internalAction];
// 設置代理,處理根控制器下的業務
pan.delegate = self;
[self.view addGestureRecognizer:pan];
// 清空原生手勢,以此避免不必要bug
self.interactivePopGestureRecognizer.enabled = NO;
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
if (self.topViewController ) {
CGPoint tPoint = [(UIPanGestureRecognizer *)gestureRecognizer translationInView:gestureRecognizer.view];
if (tPoint.x >= 0) {
CGFloat y = fabs(tPoint.y);
CGFloat x = fabs(tPoint.x);
CGFloat af = 30.0f/180.0f * M_PI;
CGFloat tf = tanf(af);
if ((y/x) <= tf) {
return YES;
}
return NO;
}else{
return NO;
}
}
}
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
return self.childViewControllers.count > 1;
}
@end
方案三:完全自定義
這是蘋果官方在WWDC上提倡的方法,靈活性高。可以高度自定義push和pop轉場動畫。
這種方法需要我們徹底實現側滑返回,那我們的思路就是:
- 先給view添加一個UIPanGestureRecognizer手勢;
- 再自定義該手勢的觸發方法,該方法里實現了側滑。
- 先創建一個BaseViewController,給該控制器的view添加拖動手勢;
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationController.delegate = self; // 設置navigationController的代理為self,并實現其代理方法
self.view.userInteractionEnabled = YES;
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(backHandle:)];
[self.view addGestureRecognizer:panGesture];
}
- (void)backHandle:(UIPanGestureRecognizer *)recognizer
{
[self customControllerPopHandle:recognizer];
}
2.側滑手勢會觸發這個回調方法;
- (void)customControllerPopHandle:(UIPanGestureRecognizer *)recognizer
{
if(self.navigationController.childViewControllers.count == 1)
{
return;
}
// _interactiveTransition就是代理方法2返回的交互對象,我們需要更新它的進度來控制POP動畫的流程。(以手指在視圖中的位置與屏幕寬度的比例作為進度)
CGFloat process = [recognizer translationInView:self.view].x/self.view.bounds.size.width;
process = MIN(1.0, MAX(0.0, process));
if(recognizer.state == UIGestureRecognizerStateBegan)
{
// 此時,創建一個UIPercentDrivenInteractiveTransition交互對象,來控制整個過程中動畫的狀態
_interactiveTransition = [[UIPercentDrivenInteractiveTransition alloc] init];
[self.navigationController popViewControllerAnimated:YES];
}
else if(recognizer.state == UIGestureRecognizerStateChanged)
{
[_interactiveTransition updateInteractiveTransition:process]; // 更新手勢完成度
}
else if(recognizer.state == UIGestureRecognizerStateEnded ||recognizer.state == UIGestureRecognizerStateCancelled)
{
// 手勢結束時,若進度大于0.5就完成pop動畫,否則取消
if(process > 0.5)
{
[_interactiveTransition finishInteractiveTransition];
}
else
{
[_interactiveTransition cancelInteractiveTransition];
}
_interactiveTransition = nil;
}
}
3.實現UINavigationControllerDelegate的兩個協議方法,分別返回自定義動畫需要的兩個重要對象;
// 代理方法1:
// 返回一個實現了UIViewControllerAnimatedTransitioning協議的對象 ,即完成轉場動畫的對象
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
{
if(operation == UINavigationControllerOperationPop) // 若operation是pop,就返回我們自定義的轉場動畫對象
{
return [[POPAnimation alloc] init];
}
return nil;
}
// 代理方法2
// 返回一個實現了UIViewControllerInteractiveTransitioning協議的對象,即完成動畫交互(動畫進度)的對象
- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController
{
if([animationController isKindOfClass:[POPAnimation class]])
{
return _interactiveTransition;
}
return nil;
}
4.創建一個自定義動畫類:POPAnimation。這是自定義動畫的核心
自定義動畫類,即一個實現了UIViewControllerAnimatedTransitioning協議的類。
實現該協議的兩個方法,一個返回動畫執行時間,一個自定義動畫。
#import "POPAnimation.h"
@interface POPAnimation ()
@end
@implementation POPAnimation
// 實現兩個協議的方法
// 返回動畫執行的時間
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
return 0.25;
}
//
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
__block UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; // 動畫來自哪個vc
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; // 轉場到哪個vc
// 轉場動畫是兩個控制器視圖的動畫,需要一個containerView作為“舞臺”
UIView *containerView = [transitionContext containerView];
[containerView insertSubview:toVC.view belowSubview:fromVC.view];
NSTimeInterval duration = [self transitionDuration:transitionContext]; // 獲取動畫執行時間(實現的協議方法)
// 執行動畫,讓fromVC的view移動到屏幕最右側
[UIView animateWithDuration:duration animations:^{
fromVC.view.transform = CGAffineTransformMakeTranslation([UIScreen mainScreen].bounds.size.width, 0);
} completion:^(BOOL finished) {
// 當動畫執行完時,這個方法必須要調用,否則系統會認為你的其余操作都在動畫執行過程中
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
@end
方案四:用第三方
把里面的分類直接拖進項目就可以
https://github.com/forkingdog/FDFullscreenPopGesture