单手使用iPhone的时候,界面返回往往是一个比较蛋疼的事情,IOS7之后苹果推出了自带的滑动返回上一级的功能,但是这只支持屏幕左边缘拖动,也比较麻烦。QQ、微信等很多软件中界面都允许在全屏幕上滑动返回上一级,所以经研究和借鉴了一些国外博客后,决定写一个相对功能实现比较容易的全屏幕滑动返回上一级的导航栏控件。
先说一下思路吧:
首先,系统自带的滑动返回功能只有在导航栏控制器中能够使用。所以,这个功能只能添加在一个UINavigationController中。我们可以通过写一个UINavigationController的子类或者扩展类的方式来实现。
其次,每当我们push到下一个界面的时候,会通过绘画的方式截取当前界面的图像并存储在内存中。由于是侧滑返回,我们需要给这个界面添加一个滑动手势,当触发滑动事件的时候渐渐显示存储在内存中的上一个界面的图像。然后判断滑动距离,当滑动完成时滑动距离满足返回上一页的要求的时候,就实现pop返回。
实现步骤:
1、创建一个UINavigationViewController的子类TNPanNavigationController
@interface TNPanNavigationController : UINavigationController
2、构建一个存放截屏图片的数组 并在TNPanNavigationController初始化的时候一起进行初始化,并在此时为TNPanNavigationController添加滑动手势
@interface TNPanNavigationController ()
{
NSMutableArray *_screenShots; //截屏图片数组
}
-(instancetype)initWithRootViewController:(UIViewController *)rootViewController{
self = [super initWithRootViewController:rootViewController];
if (self) {
_screenShots = [NSMutableArray array];
UIPanGestureRecognizer *panGestureRecognizer =
[[UIPanGestureRecognizer alloc]initWithTarget:self
action:@selector(handlePanGesture:)];
[self.view addGestureRecognizer:panGestureRecognizer];
}
return self;
}
3、截屏方式
//获取截屏图片
- (UIImage *)getCurrentScreenShot{
UIGraphicsBeginImageContextWithOptions(KEYWINDOW_BOUNDS.size, NO, 0.0);
[KEYWINDOW.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
4、编写滑动事件,调取_screenShots中上一个界面的图像并设置透明度为0.5,当触发滑动事件的时候,随着水平滑动方向上偏移量的变化,上一个界面不断移入手机界面中并且透明度逐渐升高。
当滑动事件结束之后,判断水平方向上偏移量的变化,该偏移量是否允许返回上一个界面。
//滑动事件
- (void)handlePanGesture:(UIPanGestureRecognizer *)panGestureRecognizer{
//如果是导航栏控制器的第一个界面,就不需要进行任何操作
if (self.viewControllers.count <= 1) {
return;
}
//获取上一个界面的截图
UIView *prevPageView = [KEYWINDOW viewWithTag:PREVPAGEVIEWTAG];
if (!prevPageView) {
prevPageView = [_screenShots lastObject];
prevPageView.alpha = PREVPAGEVIEW_ALPHA;
[KEYWINDOW insertSubview:prevPageView
atIndex:0];
}
//获取水平偏移量并进行位置和透明度的变化
CGPoint translation = [panGestureRecognizer translationInView:self.view];
if (translation.x > 0) {
[self.view setTransform:CGAffineTransformMakeTranslation(translation.x, 0)];
float alpha = MIN(1.0, PREVPAGEVIEW_ALPHA + translation.x / CGRectGetWidth(KEYWINDOW_BOUNDS) * (1 - PREVPAGEVIEW_ALPHA));
prevPageView.alpha = alpha;
}
//滑动完毕后判断是否返回
if (panGestureRecognizer.state == UIGestureRecognizerStateEnded) {
if (translation.x > MAXMOVE) {
//触发返回操作
[self popViewControllerWithSelfAnimation:YES];
}
else{
//恢复原状
[self backToOriginal];
}
}
}
//不进行返回,并恢复原状
- (void)backToOriginal{
UIView *prevPageView = [_screenShots lastObject];
prevPageView.alpha = PREVPAGEVIEW_ALPHA;
[UIView animateWithDuration:BACKDURATION
delay:0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
self.view.transform = CGAffineTransformMakeTranslation(0, 0);
prevPageView.alpha = PREVPAGEVIEW_ALPHA;
}
completion:^(BOOL finished) {
}];
}
5、重写UINavigationViewController的push和pop方法。由于界面返回的时候可能已经通过手势进行了一部分的返回滑动动画,所以这时候不能再使用自带的滑动返回动画,需要继续调用自己写的滑动动画,所以需要重写pop方法。而由于每个界面进行页面跳转的时候都需要记录一个新的页面截图。而只有当页面真正进行跳转的时候才需要执行截图,需要写在push方法中,所以
需要重写push方法
-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
//移除
UIView *prevPageView = [KEYWINDOW viewWithTag:PREVPAGEVIEWTAG];
if (prevPageView) {
[prevPageView removeFromSuperview];
}
//添加截图
prevPageView = [[UIImageView alloc]initWithImage:[self getCurrentScreenShot]];
prevPageView.tag = PREVPAGEVIEWTAG;
[_screenShots addObject:prevPageView];
//页面跳转
[super pushViewController:viewController
animated:animated];
}
-(void)popViewControllerWithSelfAnimation:(BOOL)animated{
if (self.viewControllers.count <= 1) {
return;
}
//获取截图
UIView *prevPageView = [KEYWINDOW viewWithTag:PREVPAGEVIEWTAG];
if (!prevPageView) {
prevPageView = [_screenShots lastObject];
prevPageView.alpha = PREVPAGEVIEW_ALPHA;
[KEYWINDOW insertSubview:prevPageView
atIndex:0];
}
//页面返回
[UIView animateWithDuration:ANIMATIONDURATION
delay:0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
self.view.transform = CGAffineTransformMakeTranslation(CGRectGetWidth(KEYWINDOW_BOUNDS), 0);
prevPageView.alpha = 1.0f;
}
completion:^(BOOL finished) {
[super popViewControllerAnimated:NO];
self.view.transform = CGAffineTransformMakeTranslation(0, 0);
[prevPageView removeFromSuperview];
[_screenShots removeLastObject];
}];
}
这么一来就OK了,只需要调用TNPanNavigationController就可以实现整个界面侧滑返回上一页的功能了。
demo已经上传优快云了
地址如下:http://download.youkuaiyun.com/detail/u013531246/8724865
最后,谢谢大家的阅读,希望多多订阅博主的博客。