首先iOS7以后系統(tǒng)默認(rèn)自帶了側(cè)滑功能兽间,當(dāng)用戶在界面的左邊滑動(dòng)的時(shí)候卖氨,就會(huì)有側(cè)滑功能朝刊。
但是如果我們從從導(dǎo)航控制器的返回按鈕酬屉,就發(fā)現(xiàn)系統(tǒng)所帶的側(cè)滑返回功能無(wú)法使用半等,而且有些功能不盡人意.系統(tǒng)自定義的優(yōu)點(diǎn)在于,當(dāng)界面中有其它易沖突手勢(shì)
(像某控制器界面本身的輕掃或左滑右滑手勢(shì))時(shí),系統(tǒng)滑動(dòng)方法是邊緣手勢(shì)酱鸭,與其它手勢(shì)的作用區(qū)域可能會(huì)有不同吗垮,會(huì)有益于解決這些沖突。
所以有以下自定義方法凹髓。
1.全屏手勢(shì)滑動(dòng)
- (void)viewDidLoad {
????? [super viewDidLoad];
????? // 獲取系統(tǒng)自帶滑動(dòng)手勢(shì)的target對(duì)象
????? id target = self.interactivePopGestureRecognizer.delegate;
????? // 創(chuàng)建全屏滑動(dòng)手勢(shì)烁登,調(diào)用系統(tǒng)自帶滑動(dòng)手勢(shì)的target的action方法
??????? UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:target action:@selector(handleNavigationTransition:)];
??? // 設(shè)置手勢(shì)代理,攔截手勢(shì)觸發(fā)
???? pan.delegate = self;
// 給導(dǎo)航控制器的view添加全屏滑動(dòng)手勢(shì)
[self.view addGestureRecognizer:pan];
// 禁止使用系統(tǒng)自帶的滑動(dòng)手勢(shì)
self.interactivePopGestureRecognizer.enabled = NO;
}
// 什么時(shí)候調(diào)用:每次觸發(fā)手勢(shì)之前都會(huì)詢問(wèn)下代理蔚舀,是否觸發(fā)饵沧。
// 作用:攔截手勢(shì)觸發(fā)
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
// 注意:只有非根控制器才有滑動(dòng)返回功能,根控制器沒(méi)有赌躺。
// 判斷導(dǎo)航控制器是否只有一個(gè)子控制器狼牺,如果只有一個(gè)子控制器,肯定是根控制器
if (self.childViewControllers.count == 1) {
// 表示用戶在根控制器界面礼患,就不需要觸發(fā)滑動(dòng)手勢(shì)是钥,
return NO;
}
return YES;
}
該方法實(shí)現(xiàn)了全屏手勢(shì)滑動(dòng),但是缅叠,當(dāng)當(dāng)前視圖有其它手勢(shì)時(shí)可能產(chǎn)生沖突悄泥。
2.部分視圖沒(méi)有右滑返回
@property (nonatomic,strong) UIViewController *currentShowVC;
-(void)viewWillAppear:(BOOL)animated
{
self.navigationController.navigationBarHidden=YES;
[_firstVC hidenLabel];
//設(shè)置代理
self.navigationController.interactivePopGestureRecognizer.delegate =(id)self;
//啟用系統(tǒng)自帶的滑動(dòng)手勢(shì)
self.navigationController.interactivePopGestureRecognizer.enabled = YES;
//判斷當(dāng)在視圖棧的第幾個(gè)是不用手勢(shì)返回,一般為1肤粱,我這里因?yàn)橛幸粋€(gè)空白的跟視圖所以設(shè)置為2
if (self.navigationController.viewControllers.count == 2){
//將當(dāng)前導(dǎo)航控制器置空
self.currentShowVC = Nil;
}else{
self.currentShowVC = self;
}
}
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer == self.navigationController.interactivePopGestureRecognizer) {
//當(dāng)前導(dǎo)航控制器是根視圖控制器
//the most important
return (self.currentShowVC == self.navigationController.topViewController);
}
return YES;
}
關(guān)于部分界面沒(méi)有返回手勢(shì):
1.在當(dāng)前界面的
-(void)viewDidAppear:(BOOL)animated
{
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}
2.在下一個(gè)需要用到返回手勢(shì)的界面
-(void)viewWillAppear:(BOOL)animated
{
//設(shè)置代理
self.navigationController.interactivePopGestureRecognizer.delegate =(id)self;
//啟用系統(tǒng)自帶的滑動(dòng)手勢(shì)
self.navigationController.interactivePopGestureRecognizer.enabled = YES;
}
3.自定義返回動(dòng)畫(huà)
.h文件
#import
@interface AnimatedNavigationController : UINavigationController
@end
.m文件
#import "AnimatedNavigationController.h"
@interface AnimatedNavigationController ()
@end
#import "ViewController.h"
@interface AnimatedNavigationController ()
{
// 屏幕截圖
UIImageView *_screenshotImgView;
// 截圖上面的黑色半透明遮罩
UIView *_coverView;
// 存放所有截圖
NSMutableArray *_screenshotImgs;
}
@end
@implementation AnimatedNavigationController
- (void)viewDidLoad
{
[super viewDidLoad];
// 1,創(chuàng)建Pan手勢(shì)識(shí)別器,并綁定監(jiān)聽(tīng)方法
UIPanGestureRecognizer *panGestureRec = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panGestureRec:)];
// 為導(dǎo)航控制器的view添加Pan手勢(shì)識(shí)別器
[self.view addGestureRecognizer:panGestureRec];
// 2.創(chuàng)建截圖的ImageView
_screenshotImgView = [[UIImageView alloc] init];
// app的frame是除去了狀態(tài)欄高度的frame
_screenshotImgView.frame = [UIScreen mainScreen].applicationFrame;
//(0 20; 320 460);
// 3.創(chuàng)建截圖上面的黑色半透明遮罩
_coverView = [[UIView alloc] init];
// 遮罩的frame就是截圖的frame
_coverView.frame = _screenshotImgView.frame;
// 遮罩為黑色
_coverView.backgroundColor = [UIColor blackColor];
// 4.存放所有的截圖數(shù)組初始化
_screenshotImgs = [NSMutableArray array];
}
// 重寫(xiě)push方法,在push之前 先截取圖片
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
// 只有在導(dǎo)航控制器里面有子控制器的時(shí)候才需要截圖
if (self.viewControllers.count >= 1) {
// 調(diào)用自定義方法,使用上下文截圖
[self screenShot];
}
// 截圖完畢之后,才調(diào)用父類(lèi)的push方法
[super pushViewController:viewController animated:YES];
}
// 使用上下文截圖,并使用指定的區(qū)域裁剪,模板代碼
- (void)screenShot
{
// 將要被截圖的view,即窗口的根控制器的view(必須不含狀態(tài)欄,默認(rèn)ios7中控制器是包含了狀態(tài)欄的)
UIViewController *beyondVC = (UIViewController *)self.view.window.rootViewController;
// 背景圖片 總的大小
CGSize size = beyondVC.view.frame.size;
// 開(kāi)啟上下文,使用參數(shù)之后,截出來(lái)的是原圖(YES? 0.0 質(zhì)量高)
UIGraphicsBeginImageContextWithOptions(size, YES, 0.0);
// 要裁剪的矩形范圍
CGRect rect = CGRectMake(0, -20.8, size.width, size.height + 20 );
//注:iOS7以后renderInContext:由drawViewHierarchyInRect:afterScreenUpdates:替代
[beyondVC.view drawViewHierarchyInRect:rect? afterScreenUpdates:NO];
// 從上下文中,取出UIImage
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
// 添加截取好的圖片到圖片數(shù)組
[_screenshotImgs addObject:snapshot];
// 千萬(wàn)記得,結(jié)束上下文(移除棧頂?shù)幕诋?dāng)前位圖的圖形上下文)
UIGraphicsEndImageContext();
}
// 監(jiān)聽(tīng)手勢(shì)的方法,只要是有手勢(shì)就會(huì)執(zhí)行
- (void)panGestureRec:(UIPanGestureRecognizer *)panGestureRec
{
// 如果當(dāng)前顯示的控制器已經(jīng)是根控制器了弹囚,不需要做任何切換動(dòng)畫(huà),直接返回
if(self.topViewController == self.viewControllers[0]) return;
// 判斷pan手勢(shì)的各個(gè)階段
switch (panGestureRec.state) {
case UIGestureRecognizerStateBegan:
// 開(kāi)始拖拽階段
[self dragBegin];
break;
case UIGestureRecognizerStateEnded:
// 結(jié)束拖拽階段
[self dragEnd];
break;
default:
// 正在拖拽階段
[self dragging:panGestureRec];
break;
}
}
#pragma mark 開(kāi)始拖動(dòng),添加圖片和遮罩
- (void)dragBegin
{
// 重點(diǎn),每次開(kāi)始Pan手勢(shì)時(shí),都要添加截圖imageview 和 遮蓋cover到window中
[self.view.window insertSubview:_screenshotImgView atIndex:0];
[self.view.window insertSubview:_coverView aboveSubview:_screenshotImgView];
// 并且,讓imgView顯示截圖數(shù)組中的最后(最新)一張截圖
_screenshotImgView.image = [_screenshotImgs lastObject];
}
// 默認(rèn)的將要進(jìn)行縮放的截圖的初始比例
#define kDefaultScale 0.6
// 默認(rèn)的將要變透明的遮罩的初始透明度(全黑)
#define kDefaultAlpha 1.0
// 當(dāng)拖動(dòng)的距離,占了屏幕的總寬高的3/4時(shí), 就讓imageview完全顯示,遮蓋完全消失
#define kTargetTranslateScale 0.75
#pragma mark 正在拖動(dòng),動(dòng)畫(huà)效果的精髓,進(jìn)行縮放和透明度變化
- (void)dragging:(UIPanGestureRecognizer *)pan
{
// 得到手指拖動(dòng)的位移
CGFloat offsetX = [pan translationInView:self.view].x;
// 只允許往右邊拖,禁止向左拖
if (offsetX < 0) offsetX = 0;
// 讓整個(gè)view都平移???? // 挪動(dòng)整個(gè)導(dǎo)航view
self.view.transform = CGAffineTransformMakeTranslation(offsetX, 0);
// 計(jì)算目前手指拖動(dòng)位移占屏幕總的寬高的比例,當(dāng)這個(gè)比例達(dá)到3/4時(shí), 就讓imageview完全顯示领曼,遮蓋完全消失
double currentTranslateScaleX = offsetX/self.view.frame.size.width;
// 讓imageview縮放,默認(rèn)的比例+(當(dāng)前平衡比例/目標(biāo)平衡比例)*(1-默認(rèn)的比例)
double scale = kDefaultScale + (currentTranslateScaleX/kTargetTranslateScale) * (1 - kDefaultScale);
// 已經(jīng)達(dá)到原始大小了,就可以了,不用放得更大了
if (scale > 1) scale = 1;
_screenshotImgView.transform = CGAffineTransformMakeScale(scale, scale);
// 讓遮蓋透明度改變,直到減為0,讓遮罩完全透明,默認(rèn)的比例-(當(dāng)前平衡比例/目標(biāo)平衡比例)*默認(rèn)的比例
double alpha = kDefaultAlpha - (currentTranslateScaleX/kTargetTranslateScale) * kDefaultAlpha;
_coverView.alpha = alpha;
}
#pragma mark 結(jié)束拖動(dòng),判斷結(jié)束時(shí)拖動(dòng)的距離作相應(yīng)的處理,并將圖片和遮罩從父控件上移除
- (void)dragEnd
{
// 取出挪動(dòng)的距離
CGFloat translateX = self.view.transform.tx;
// 取出寬度
CGFloat width = self.view.frame.size.width;
if (translateX <= width * 0.5) {
// 如果手指移動(dòng)的距離還不到屏幕的一半,往左邊挪 (彈回)
[UIView animateWithDuration:0.3 animations:^{
// 重要~~讓被右移的view彈回歸位,只要清空transform即可辦到
self.view.transform = CGAffineTransformIdentity;
// 讓imageView大小恢復(fù)默認(rèn)的scale
_screenshotImgView.transform = CGAffineTransformMakeScale(kDefaultScale, kDefaultScale);
// 讓遮蓋的透明度恢復(fù)默認(rèn)的alpha 1.0
_coverView.alpha = kDefaultAlpha;
} completion:^(BOOL finished) {
// 重要,動(dòng)畫(huà)完成之后,每次都要記得 移除兩個(gè)view,下次開(kāi)始拖動(dòng)時(shí),再添加進(jìn)來(lái)
[_screenshotImgView removeFromSuperview];
[_coverView removeFromSuperview];
}];
} else {
// 如果手指移動(dòng)的距離還超過(guò)了屏幕的一半,往右邊挪
[UIView animateWithDuration:0.3 animations:^{
// 讓被右移的view完全挪到屏幕的最右邊,結(jié)束之后,還要記得清空view的transform
self.view.transform = CGAffineTransformMakeTranslation(width, 0);
// 讓imageView縮放置為1
_screenshotImgView.transform = CGAffineTransformMakeScale(1, 1);
// 讓遮蓋alpha變?yōu)?,變得完全透明
_coverView.alpha = 0;
} completion:^(BOOL finished) {
// 重要~~讓被右移的view完全挪到屏幕的最右邊,結(jié)束之后,還要記得清空view的transform,不然下次再次開(kāi)始drag時(shí)會(huì)出問(wèn)題,因?yàn)関iew的transform沒(méi)有歸零
self.view.transform = CGAffineTransformIdentity;
// 移除兩個(gè)view,下次開(kāi)始拖動(dòng)時(shí),再加回來(lái)
[_screenshotImgView removeFromSuperview];
[_coverView removeFromSuperview];
// 執(zhí)行正常的Pop操作:移除棧頂控制器,讓真正的前一個(gè)控制器成為導(dǎo)航控制器的棧頂控制器
[self popViewControllerAnimated:NO];
// 重要~記得這時(shí)候,可以移除截圖數(shù)組里面最后一張沒(méi)用的截圖了
[_screenshotImgs removeLastObject];
}];
}
}
@end
demo地址: https://github.com/hyf12138/NavigationDemo.git