在實際開發過程中構建我們項目框架時為了用戶體驗好和個性化往往需要手勢返回的功能,雖然ios7以後的系統支持這個但是系統有些不足如:支持手勢返回需要ios7.0以上系統,手勢非要從左邊緣開始拉,觸摸拉開效果不能自定義,然而我們今天就給大家開發一款支持手勢返回沒有系統要求(ios5.0以上),從哪裡可以觸摸返回可以自定義的一個導航框架。
我這個是以淘寶app的導航框架為例開發。
WHC_NavigationController.h頭文件如下:
//
// WHC_NavigationController.h
// WHC_NavigationController
//
// Created by 吳海超 on 15/4/13.
// Copyright (c) 2015年 吳海超. All rights reserved.
//
#import
#define KWHC_Nav_Main_VC_Name (@"MyTabBarVC") //主控制器類名
#define KWHC_Nav_Touch_Back_Rate (0.4) //觸摸拖拽可以返回條件比例
#define KWHC_Nav_Pop_Form_Border (-1) //note > 0 from border pull < 0 any where pull diatancex > 30
@interface WHC_NavigationController : UINavigationController
//單例默認用KWHC_Nav_Main_VC_Name作為主控制器
+ (instancetype)sharedInstance;
//單例自定義主控制器
+ (instancetype)sharedInstanceWithMainVC:(UIViewController*)mainVC;
@end
WHC_NavigationController.m源文件如下:
//
// WHC_NavigationController.m
// SideMenu
//
// Created by 吳海超 on 15/4/13.
// Copyright (c) 2015年 吳海超. All rights reserved.
//
#import "WHC_NavigationController.h"
#define KWHC_NAV_LEFT_VIEW_TAG (203994850) //左邊view的tag
#define KWHC_NAV_LEFT_PUSH_VIEW_TAG (KWHC_NAV_LEFT_VIEW_TAG + 1) //左邊pushView的tag
#define KWHC_NAV_NAVBAR_TAG (KWHC_NAV_LEFT_VIEW_TAG + 2) //導航條的tag
#define KWHC_NAV_ANIMATION_DURING (0.3) //pop或者push動畫周期
#define KWHC_NAV_POP_VIEW_CENTERX_OFFSET (0) //popView中心x軸偏移 (禁止修改值)
#define KWHC_NAV_PUSH_VIEW_CENTERX_OFFSET (0) //pushView中心x偏移 (禁止修改值)
#define KWHC_NAV_POP_VIEW_ALPHA (0.5) //popview透明系數
#define KWHC_NAV_ALLOW_PULL_DISTANCE (30.0) //可以拉開允許的距離
@interface WHC_NavigationController (){
UIPanGestureRecognizer * _panGesture; //手勢
CGFloat _currentTx; //存儲當前觸摸x值
NSMutableArray * _snapshootList; //快照數組
UIView * _popView; //彈出view
UIView * _pushView; //壓站view
UIView * _topView; //頂部視圖
BOOL _willOpen; //是否將要拉開
BOOL _didOpen; //是否已經拉開
BOOL _isTouchPop; //是否觸摸彈出
}
@end
static WHC_NavigationController * whc_navigation;
@implementation WHC_NavigationController
//單例實現
+ (instancetype)sharedInstance{
UIViewController * rootVC = [[NSClassFromString(KWHC_Nav_Main_VC_Name) alloc]init];
// UIViewController * rootVC = [[NSClassFromString(KWHC_Side_Menu_N_Main_VC_Name) alloc]initWithNibName:KWHC_Nav_Main_VC_Name bundle:nil];
return [WHC_NavigationController sharedInstanceWithMainVC:rootVC];
}
+ (instancetype)sharedInstanceWithMainVC:(UIViewController*)mainVC{
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
whc_navigation = [[WHC_NavigationController alloc]initWithRootViewController:mainVC];
whc_navigation.delegate = whc_navigation;
});
return whc_navigation;
}
#pragma mark - initMothed
//重載父類初始化方法
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController{
self = [super initWithRootViewController:rootViewController];
if(self){
[self registPanGesture:YES];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder{
self = [super initWithCoder:aDecoder];
if(self != nil){
[self registPanGesture:YES];
}
return self;
}
- (instancetype)init{
self = [super init];
if(self != nil){
[self registPanGesture:YES];
}
return self;
}
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if(self){
[self registPanGesture:YES];
}
return self;
}
#pragma mark - gestureMothed
//注冊手勢事件
- (void)registPanGesture:(BOOL)b{
self.interactivePopGestureRecognizer.enabled = NO;
if(b){
if(_panGesture == nil){
_panGesture = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(handlePanGesture:)];
[_panGesture delaysTouchesBegan];
[self.view addGestureRecognizer:_panGesture];
}
}else{
if(_panGesture != nil){
[self.view removeGestureRecognizer:_panGesture];
_panGesture = nil;
}
}
}
#pragma mark - handleGesture
- (void)handlePanGesture:(UIPanGestureRecognizer*)panGesture{
switch (panGesture.state) {
case UIGestureRecognizerStateBegan:{
_currentTx = self.view.transform.tx;
}
break;
case UIGestureRecognizerStateChanged:{
CGFloat moveDistance = [panGesture translationInView:panGesture.view].x;
if(!_willOpen){
if(KWHC_Nav_Pop_Form_Border < (0)){
if(moveDistance < KWHC_NAV_ALLOW_PULL_DISTANCE){
return;
}
}else{
CGFloat touchX = [panGesture locationInView:panGesture.view].x;
if(touchX > KWHC_Nav_Pop_Form_Border){
return;
}
}
}
CGFloat oriX = [panGesture velocityInView:panGesture.view].x;
CGFloat rate = moveDistance / CGRectGetWidth(self.view.frame);
if(oriX > 0){//open door
[self popView];
if(!_willOpen && _popView != nil){
UIView * oldPopView = [self.view.superview viewWithTag:KWHC_NAV_LEFT_VIEW_TAG];
if(oldPopView){
[oldPopView removeFromSuperview];
}
[self.view.superview insertSubview:_popView belowSubview:self.view];
_willOpen = YES;
}
if(_willOpen && moveDistance >= 0.0){
self.view.transform = [self initAffineTransform:moveDistance + _currentTx];
_popView.center = CGPointMake(KWHC_NAV_POP_VIEW_CENTERX_OFFSET + rate * CGRectGetWidth(_popView.frame) / 2.0, _popView.center.y);
_popView.alpha = KWHC_NAV_POP_VIEW_ALPHA * (rate + 1.0);
}
}else if(oriX < 0 && _willOpen && moveDistance >= 0.0){//close door
self.view.transform = [self initAffineTransform:moveDistance + _currentTx];
_popView.center = CGPointMake(KWHC_NAV_POP_VIEW_CENTERX_OFFSET + rate * CGRectGetWidth(_popView.frame) / 2.0, _popView.center.y);
_popView.alpha = KWHC_NAV_POP_VIEW_ALPHA * (rate + 1.0);
}else if(_willOpen){
if(moveDistance < 0.0 && self.view.transform.tx > 0){
self.view.transform = [self initAffineTransform:0.0];
_popView.center = CGPointMake(KWHC_NAV_POP_VIEW_CENTERX_OFFSET, _popView.center.y);
_popView.alpha = KWHC_NAV_POP_VIEW_ALPHA;
}
}
}
break;
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateEnded:{
if(_willOpen){
if(self.view.transform.tx / CGRectGetWidth(self.view.frame) < KWHC_Nav_Touch_Back_Rate){
[self closeLeftView:YES];
}else{
[self closeLeftView:NO];
}
}
}
break;
default:
break;
}
}
//偏移變換
- (CGAffineTransform)initAffineTransform:(CGFloat)x{
return CGAffineTransformMakeTranslation(x, 0.0);
}
//對當前控制器進行快照
- (UIImage*)snapshootNavBar:(UIViewController*)vc{
UIGraphicsBeginImageContextWithOptions(self.view.frame.size, self.view.opaque, 0.0);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * snapshootImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return snapshootImage;
}
//獲取要彈出view的快照view
- (UIView*)popView{
if(_snapshootList != nil && _snapshootList.count > 1){
_popView = [_snapshootList lastObject];
_popView.center = CGPointMake(KWHC_NAV_POP_VIEW_CENTERX_OFFSET, _popView.center.y);
_popView.alpha = KWHC_NAV_POP_VIEW_ALPHA;
}else{
_popView = nil;
}
return _popView;
}
//獲取已經push的快照view
- (UIView*)pushView{
if(_snapshootList != nil && _snapshootList.count > 0){
_pushView = [_snapshootList lastObject];
_pushView.center = CGPointMake(KWHC_NAV_PUSH_VIEW_CENTERX_OFFSET, _pushView.center.y);
_pushView.alpha = 1.0;
}else{
_pushView = nil;
}
return _pushView;
}
//pop或者取消pop操作
- (void)closeLeftView:(BOOL)isClose{
[self registPanGesture:NO];
_willOpen = NO;
UIView * mainView = self.view;
__weak typeof(self) sf = self;
if(isClose){
[UIView animateWithDuration:KWHC_NAV_ANIMATION_DURING delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
_popView.center = CGPointMake(KWHC_NAV_POP_VIEW_CENTERX_OFFSET, _popView.center.y);
_popView.alpha = KWHC_NAV_POP_VIEW_ALPHA;
mainView.transform = [sf initAffineTransform:0.0];
} completion:^(BOOL finished) {
[sf registPanGesture:YES];
[_popView removeFromSuperview];
_popView = nil;
}];
}else{//pop opeartion
[UIView animateWithDuration:KWHC_NAV_ANIMATION_DURING delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
mainView.transform = [sf initAffineTransform:CGRectGetWidth(mainView.frame)];
_popView.center = CGPointMake(CGRectGetWidth(_popView.frame) / 2.0, _popView.center.y);
_popView.alpha = 1.0;
} completion:^(BOOL finished) {
[sf registPanGesture:YES];
_isTouchPop = YES;
mainView.transform = [sf initAffineTransform:0];
[sf popViewControllerAnimated:NO];
}];
}
}
#pragma mark - viewLoad
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//重載父類push方法
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
if(_snapshootList == nil){
_snapshootList = [NSMutableArray array];
}
[_snapshootList addObject:[[UIImageView alloc]initWithImage:[self snapshootNavBar:viewController]]];
[self pushView];
//忽略第一次push操作
if(_pushView && animated && _snapshootList != nil){
__weak typeof(self) sf = self;
//防止重復push
_topView = self.topViewController.view;
_topView.userInteractionEnabled = NO;
[super pushViewController:viewController animated:NO];
self.view.transform = [self initAffineTransform:CGRectGetWidth(self.view.frame)];
UIView * oldPushView = [self.view.superview viewWithTag:KWHC_NAV_LEFT_PUSH_VIEW_TAG];
if(oldPushView){
[oldPushView removeFromSuperview];
oldPushView = nil;
}
_pushView.center = CGPointMake(CGRectGetWidth(_pushView.frame) / 2.0, _pushView.center.y);
[self.view.superview insertSubview:_pushView belowSubview:self.view];
[UIView animateWithDuration:KWHC_NAV_ANIMATION_DURING delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
_pushView.center = CGPointMake(-CGRectGetWidth(_pushView.frame) / 2.0, _pushView.center.y);
_pushView.alpha = KWHC_NAV_POP_VIEW_ALPHA;
sf.view.transform = [sf initAffineTransform:0];
} completion:^(BOOL finished) {
[_pushView removeFromSuperview];
_pushView = nil;
}];
}else{
[super pushViewController:viewController animated:animated];
}
}
//重寫父類pop方法
- (UIViewController *)popViewControllerAnimated:(BOOL)animated{
UIViewController * viewController = nil;
if(!_isTouchPop){
//不是觸摸彈出操作
__weak typeof(self) sf = self;
__block UIView * popView = [[UIImageView alloc]initWithImage:[self snapshootNavBar:nil]];
viewController = [super popViewControllerAnimated:NO];
if(popView){
self.view.transform = [self initAffineTransform:-CGRectGetWidth(self.view.frame)];
self.view.alpha = KWHC_NAV_POP_VIEW_ALPHA;
UIView * oldPopView = [self.view.superview viewWithTag:KWHC_NAV_LEFT_VIEW_TAG];
if(oldPopView){
[oldPopView removeFromSuperview];
oldPopView = nil;
}
popView.center = CGPointMake(CGRectGetWidth(popView.frame) / 2.0, popView.center.y);
[self.view.superview insertSubview:popView belowSubview:self.view];
[UIView animateWithDuration:KWHC_NAV_ANIMATION_DURING delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
popView.center = CGPointMake(CGRectGetWidth(popView.frame) * 1.5, popView.center.y);
sf.view.transform = [sf initAffineTransform:0];
sf.view.alpha = 1.0;
} completion:^(BOOL finished) {
[popView removeFromSuperview];
popView = nil;
[_snapshootList removeLastObject];
}];
}
}else{
//清除popview的快照存儲
[_snapshootList removeLastObject];
[_popView removeFromSuperview];
_popView = nil;
viewController = [super popViewControllerAnimated:animated];
}
_isTouchPop = NO;
return viewController;
}
#pragma mark - UINavigationControllerDelegate
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
}
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
if(_topView != nil){
_topView.userInteractionEnabled = YES;
}
}
@end