App开发流程之右滑返回手势功能续
上一篇记录了利用系统私有变量和方法实现右滑返回手势功能:http://www.cnblogs.com/ALongWay/p/5893515.html
这篇继续记录另一种方案:利用UINavigationController的delegate方法。
核心代理方法有如下两个:
- (nullable id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) animationController NS_AVAILABLE_IOS(7_0); - (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC NS_AVAILABLE_IOS(7_0);
第一个代理方法,要求在视图控制器变换时候,返回一个实现UIViewControllerInteractiveTransitioning协议的对象,该对象用于控制交互进度和状态。
UIKit.h/UIViewControllerTransitioning.h中已经准备好了一个这样的对象,名为UIPercentDrivenInteractiveTransition。
主要关注其如下三个方法:
- (void)updateInteractiveTransition:(CGFloat)percentComplete;//控制交互进度的百分比
- (void)cancelInteractiveTransition;//中途取消交互
- (void)finishInteractiveTransition;//完成交互
可以在一个实现了代理方法的类中,定义一个手势目标方法来调用上述方法,达到控制作为变量的UIPercentDrivenInteractiveTransition对象的目的。
主要代码如下:
- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController
{
if ([animationController isKindOfClass:[PopAnimation class]])
return _interactivePopTransition; return nil;
} - (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
{
if (operation == UINavigationControllerOperationPop)
return _popAnimation; return nil;
} - (void)handlePopGestureOperation:(UIPanGestureRecognizer *)recognizer{
//将手指在屏幕上的移动距离与屏幕宽度比例作为动画的进度
CGPoint translationInScreen = [recognizer translationInView:[UIApplication sharedApplication].keyWindow];
CGFloat progress = translationInScreen.x / [UIScreen mainScreen].bounds.size.width;
progress = MIN(1.0, MAX(0.0, progress)); if (recognizer.state == UIGestureRecognizerStateBegan) {
//新建手势交互对象
_interactivePopTransition = [[UIPercentDrivenInteractiveTransition alloc] init]; //开始执行pop动画
[_naviController popViewControllerAnimated:YES];
}else if (recognizer.state == UIGestureRecognizerStateChanged) {
//更新进度
[_interactivePopTransition updateInteractiveTransition:progress];
}else if (recognizer.state == UIGestureRecognizerStateEnded
|| recognizer.state == UIGestureRecognizerStateCancelled) {
//手势结束时如果进度大于一半,那么就完成pop操作,否则取消
if (progress > 0.5) {
[_interactivePopTransition finishInteractiveTransition];
}else {
[_interactivePopTransition cancelInteractiveTransition];
} //手势交互结束,清理对象
_interactivePopTransition = nil;
}
}
第二个代理方法,要求在操作开始时候,返回一个实现UIViewControllerAnimatedTransitioning协议的对象,该对象实现了动画内容和执行时间。
该协议有三个方法:
// This is used for percent driven interactive transitions, as well as for container controllers that have companion animations that might need to
// synchronize with the main animation.
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext;
// This method can only be a nop if the transition is interactive and not a percentDriven interactive transition.
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext; @optional // This is a convenience and if implemented will be invoked by the system when the transition context's completeTransition: method is invoked.
- (void)animationEnded:(BOOL) transitionCompleted;
第一个方法返回动画时间;第二个方法,可以得到实现了UIViewControllerContextTransitioning协议的对象;第三个方法可选,如果transitionContext对象调用了completeTransition:方法,该代理方法会被调用。
重点是第二个方法的transitionContext对象,其重要的方法如下:
// The view in which the animated transition should take place.
- (nullable UIView *)containerView;//动画所在的容器view
- (BOOL)transitionWasCancelled;//转换是否取消// This must be called whenever a transition completes (or is cancelled.)
// Typically this is called by the object conforming to the
// UIViewControllerAnimatedTransitioning protocol that was vended by the transitioning
// delegate. For purely interactive transitions it should be called by the
// interaction controller. This method effectively updates internal view
// controller state at the end of the transition.
- (void)completeTransition:(BOOL)didComplete;//完成转换,必须在动画完成时调用 // Currently only two keys are defined by the
// system - UITransitionContextToViewControllerKey, and
// UITransitionContextFromViewControllerKey.
// Animators should not directly manipulate a view controller's views and should
// use viewForKey: to get views instead.
- (nullable __kindof UIViewController *)viewControllerForKey:(NSString *)key;//取得视图控制器 // Currently only two keys are defined by the system -
// UITransitionContextFromViewKey, and UITransitionContextToViewKey
// viewForKey: may return nil which would indicate that the animator should not
// manipulate the associated view controller's view.
- (nullable __kindof UIView *)viewForKey:(NSString *)key NS_AVAILABLE_IOS(8_0);//取得视图控制器的view,兼容iOS7,可以直接用上面取到的viewController.view
创建一个实现UIViewControllerAnimatedTransitioning协议的对象PopAnimation,重要代码如下:
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
return _transitionDuration;
}
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
_transitionContext = transitionContext;
//当前控制器
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
//动画结束显示的控制器
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
//执行交互动画的容器view
UIView *containerView = [transitionContext containerView];
[containerView insertSubview:toViewController.view belowSubview:fromViewController.view];
NSTimeInterval duration = [self transitionDuration:transitionContext];
switch (_popAnimationType) {
case PopAnimationTranslation: {
CGFloat containerWidth = containerView.frame.size.width;
toViewController.view.transform = CGAffineTransformMakeTranslation(-containerWidth / 4.0, );
[UIView animateWithDuration:duration animations:^{
toViewController.view.transform = CGAffineTransformMakeTranslation(, );
fromViewController.view.transform = CGAffineTransformMakeTranslation(containerWidth, );
}completion:^(BOOL finished) {
//动画结束,必须调用此方法
[transitionContext completeTransition:!transitionContext.transitionWasCancelled];
}];
break;
}
case PopAnimationFlip: {
[UIView beginAnimations:@"View Flip" context:nil];
[UIView setAnimationDuration:duration];
[UIView setAnimationDelegate:self];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:containerView cache:YES];
[UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:)];
[UIView commitAnimations];
[containerView exchangeSubviewAtIndex: withSubviewAtIndex:];
break;
}
case PopAnimationCube: {
CATransition *transiton = [CATransition animation];
transiton.type = @"cube";
transiton.subtype = @"fromLeft";
transiton.duration = duration;
transiton.removedOnCompletion = NO;
transiton.fillMode = kCAFillModeForwards;
transiton.delegate = self;
[containerView.layer addAnimation:transiton forKey:nil];
[containerView exchangeSubviewAtIndex: withSubviewAtIndex:];
break;
}
}
}
- (void)animationDidStop:(CATransition *)anim finished:(BOOL)flag {
[_transitionContext completeTransition:!_transitionContext.transitionWasCancelled];
}
剩下的工作:
在上一篇的基础上,修改UINavigationController的base_pushViewController方法
- (void)base_pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if (![self.interactivePopGestureRecognizer.view.gestureRecognizers containsObject:self.base_panGestureRecognizer]) {
[self.interactivePopGestureRecognizer.view addGestureRecognizer:self.base_panGestureRecognizer]; self.base_panGestureRecognizer.delegate = [self base_panGestureRecognizerDelegateObject]; self.base_currentDelegateObject = [[NavigationControllerDelegateObject alloc] initWithUINavigationController:self]; [self.base_panGestureRecognizer addTarget:self.base_currentDelegateObject action:@selector(handlePopGestureOperation:)]; self.interactivePopGestureRecognizer.enabled = NO;
} [self base_setupViewControllerBasedNavigationBarAppearanceIfNeeded:viewController]; if (![self.viewControllers containsObject:viewController]) {
[self base_pushViewController:viewController animated:animated];
}
}
可以看到,去除了私有变量和方法的调用,自定义了名为NavigationControllerDelegateObject的对象,其实现了UINavigationControllerDelegate协议,并实现了pan手势的处理方法。在此,我暴露了该属性对象,考虑到以后可能会增加或者修改UINavigationControllerDelegate的实现协议,可以为该对象增加分类来达到目的;还有一个目的,便于修改popAnimation的动画类型和执行时间。

总结:
两篇记录,从不同的方向,记录了右滑返回手势功能的实现。利用系统变量和方法,方便快捷;利用协议方法,高度自定义。
但是,利用协议方法,需要处理的方面比较多。虽然上述已经记录了实现核心,示例代码中也有详细代码,但是仍然还有很多方面未考虑。
目前,我发现的一些问题:
1.导航栏交互效果也需要自行实现,暂未实现原生效果
2.导航栏由隐藏到显示的效果,有时会出现异常
3.如果带有bottomBar,效果不理想
如果有好的解决办法,希望告知,谢谢。
base项目已更新:git@github.com:ALongWay/base.git
项目增加了新的UINavigationController+PopOperation分类,但是未引用到项目中,需要的同学,可先删除PopGesture分类的引用,再添加该分类的应用。
App开发流程之右滑返回手势功能续的更多相关文章
- App开发流程之右滑返回手势功能
iOS7以后,导航控制器,自带了从屏幕左边缘右滑返回的手势功能. 但是,如果自定义了导航栏返回按钮,这项功能就失效了,需要自行实现.又如果需要修改手势触发范围,还是需要自行实现. 广泛应用的一种实现方 ...
- iOS开发之自定义导航栏返回按钮右滑返回手势失效的解决
我相信针对每一个iOS开发者来说~除了根视图控制器外~所有的界面通过导航栏push过去的界面都是可以通过右滑来返回上一个界面~其实~在很多应用和APP中~用户已经习惯了这个功能~然而~作为开发者的我们 ...
- 第二十六篇、因为自定item(nav)而使系统右滑返回手势失效的解决方法
@interface ViewController () <uigesturerecognizerdelegate> @end@implementation ViewController ...
- 解决右滑返回手势和UIScrollView中的手势冲突
当在一个viewController中添加了scrollView或者tableView的时候,贴边侧滑返回的时候会首先触发滚动而失效,要解决这个问题,需要通过requireGestureRecogni ...
- 关于iOS自定义返回按钮右滑返回手势失效的解决:
在viewDidLoad方法里面添加下面这一句代码即可 self.navigationController.interactivePopGestureRecognizer.delegate=(id)s ...
- 全新的手势,侧滑返回、全局右滑返回都OUT啦!
前言 Android快速开发框架-ZBLibrary 最近将以前的 全局右滑返回 手势功能改成了 底部左右滑动手势. 为什么呢?为了解决滑动返回手势的问题. 目前有3种滑动返回手势 一.侧滑返回 代表 ...
- iOS系统右滑返回全局控制方案
前言 今天有个小需求,在点击导航条上的返回按钮之前要调用某个API,并弹出UIAlertView来显示,根据用户的选项判断是否是返回还是继续留在当前控制器.举个简单的例子,当点击导航条上的左上角返回按 ...
- swift 关于FDFullscreenPopGesture的右滑返回
关于导航栏右滑返回的工具库 FDFullscreenPopGesture 是 OC 版本,用了 runtime 等各种骚操作 关于 swift ,我在 UINavigationController 的 ...
- iOS页面右滑返回的实现方法总结
1.边缘触发的系统方法 ①系统返回按钮 self.navigationController.interactivePopGestureRecognizer.enabled = YES; ②自定义返回 ...
随机推荐
- 使用Unity3d做异形窗口
项目马上上线,因为之前的登录器是使用VS2010的MFC做的,在很多电脑上会提示缺失mfcXXXX.dll,中间找寻这种解决方案,最后确定将vcredist2010_x86和我的程序打包到安装包里面, ...
- 关于js性能
1,声明变量要赋初值2,尽量避免声明全局变量,可以减少与系统的重名3,当编写大量js代码时,难免会遇到命名冲突,这是可以通过模拟命名空间方式 来避免冲突4,尽量避免使用全局变量,搜索全局变量是 ...
- c#通过oledb获取excel文件表结构信息
这个问题来自论坛提问,同理可以获得access等数据库的表结构信息. using System; namespace ConsoleApplication11 { class Program { pu ...
- iOS阶段学习第27天笔记(UIButton-UIImageView的介绍)
iOS学习(UI)知识点整理 一.关于UIButton的介绍 1)概念:UIButton 是一种常用的控件,通过点击触发相应的功能 2)UIButton 的几种常用的状态 1.UICon ...
- Software license key and activation
http://stackoverflow.com/questions/16222674/software-license-key-and-activationhttps://github.com/La ...
- PHP中用GD绘制饼图
PHP中用GD绘制饼图,绘制的类见代码: Class Chart{ private $image; // 定义图像 private $title; // 定义标题 private $ydata; // ...
- Java基础复习笔记系列 八 多线程编程
Java基础复习笔记系列之 多线程编程 参考地址: http://blog.csdn.net/xuweilinjijis/article/details/8878649 今天的故事,让我们从上面这个图 ...
- Java final方法
之所以要使用final方法,可能是出于对两方面理由的考虑.第一个是为方法"上锁",防止任何继承类改变它的本来含义.设计程序时,若希望一个方法的行为在继承期间保持不变,而且不可被覆盖 ...
- GJM : Unity3D - UI - UI边缘流光特效小技巧 [转载]
感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...
- 开源日志记录工具log4j
前言:当我们进行大的项目书写的时候或者我们选择维护程序的时候,想知道几点几时我们录入的数据有bug是那么我们就采用--------------------------->log4j记录日志的信息 ...