上一篇记录了利用系统私有变量和方法实现右滑返回手势功能:http://www.cnblogs.com/ALongWay/p/5893515.html

这篇继续记录另一种方案:利用UINavigationController的delegate方法。

核心代理方法有如下两个:

- (nullable id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationControllerinteractionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) animationController NS_AVAILABLE_IOS(7_0);- (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationControlleranimationControllerForOperation:(UINavigationControllerOperation)operationfromViewController:(UIViewController *)fromVCtoViewController:(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];//执行交互动画的容器viewUIView *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, 0);[UIView animateWithDuration:duration animations:^{toViewController.view.transform = CGAffineTransformMakeTranslation(0, 0);fromViewController.view.transform = CGAffineTransformMakeTranslation(containerWidth, 0);}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:0 withSubviewAtIndex:1];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:0 withSubviewAtIndex:1];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分类的引用,再添加该分类的应用。

转载于:https://www.cnblogs.com/ALongWay/p/5896982.html

App开发流程之右滑返回手势功能续相关推荐

  1. 【转】iOS右滑返回手势全解和最佳实施方案

    序言 在ios7以后,苹果推出了手势滑动返回功能,也就是从屏幕左侧向右滑动可返回上一个界面.大大提高了APP在大屏手机和iPad上的操作体验,场景切换更加流畅.做右滑返回手势配置时,可能会遇到的 问题 ...

  2. ios开发返回按钮消失_iOS开发之自定义导航栏返回按钮右滑返回手势失效的解决...

    我相信针对每一个iOS开发者来说~除了根视图控制器外~所有的界面通过导航栏push过去的界面都是可以通过右滑来返回上一个界面~其实~在很多应用和APP中~用户已经习惯了这个功能~然而~作为开发者的我们 ...

  3. 苹果侧边滑动返回_iOS系统右滑返回手势问题及解决方案

    在iOS7之后,苹果推出了手势滑动返回功能,也就是从屏幕左侧向右滑动可返回上一个界面.大大提高了APP在大屏手机和iPad上的操作体验,场景切换更加流畅. 常见的问题有: 1.右滑手势失效 2.右滑手 ...

  4. 禁用导航栏的右滑返回实现全屏手势返回

    今天发现项目中push 的也面的右滑都无法pop 查阅相关资料发现 导航栏右滑手势失效基本有两种情况 1: self.navigationController.interactivePopGestur ...

  5. android右滑返回动画,Android仿微信右滑返回功能的实例代码

    先上效果图,如下: 先分析一下功能的主要技术点,右滑即手势判断,当滑到一直距离时才执行返回,并且手指按下的位置是在屏幕的最左边(这个也是有一定范围的),  这些可以实现onTouchEvent来实现. ...

  6. iOS 为自定义返回按钮的页面添加右滑返回

    2019独角兽企业重金招聘Python工程师标准>>> 苹果一直都在人机交互中尽力做到极致,在iOS7中,新增加了一个小小的功能,也就是这个api:self.navigationCo ...

  7. APP开发流程实例讲解-儒释道网络电台八天开发全程-百度云深度兼容测试并进一步优化排错

    APP开发流程实例讲解-儒释道网络电台八天开发全程之 百度云深度兼容测试并进一步优化排错 APP开发流程实例讲解-儒释道网络电台八天开发全程 项目发起 功能和界面初步设定 在Android Studi ...

  8. iOS 右滑返回失效问题终极解决方案

    iOS 的右滑返回是必不可少的一项功能,否则用户体验会大打折扣,但是会经常会碰到某些页面右滑返回失效的情况,下面记录一下解决各种情况下右滑返回失效的方法: 1. 自定义返回按钮 如果页面上是自定义的返 ...

  9. ios7自定义返回按钮后,右滑返回功能失效解决方法

    ios7自定义返回按钮后,右滑返回功能失效解决方法 -(void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; //开 ...

最新文章

  1. linux脚本 程序输入,[转]Linux中shell脚本如何自动输入…
  2. 用localStorage实现登录时记住密码的功能
  3. SQL 语句中对于like 的调优
  4. 【详细解析】1033 To Fill or Not to Fill (25 分)
  5. C#并发实战Parallel.ForEach使用
  6. python需要什么包装_python学习之包装与授权
  7. 单片机c语言二进制转10进制,51单片机用C语言怎么样把八位二进制转换成十进制...
  8. Why Blink and Why not Blink
  9. python剪刀石头布_python练习案例--剪刀石头布
  10. 动态像素绘画——StarDust
  11. 【推荐】我的FLASH情结2010——浅谈FLASH WEB GAME与创业(3)
  12. 【50集全】国家地理双语纪录片-第26集【Lion】学习笔记
  13. 杭州(含嘉兴,绍兴,金华,湖州,义乌)Uber优步司机奖励政策(1月25日~1月31日)...
  14. 冷链食品追溯迫在眉睫,爱码物联3步助力冷链溯源
  15. Word实用操作技巧之文字编辑(转)
  16. 国瀚实业|工薪家庭理财资产配置攻略
  17. 怎样量产U盘,和量产之后怎么还原到普通U盘
  18. 读书笔记(2014-06)
  19. 如何证明服从卡方分布_为什么(n-1)s^2/σ^2服从自由度为n-1卡方分布?
  20. qq自定义diy名片代码复制_「正点原子FPGA连载」第六章自定义IP核-呼吸灯实验

热门文章

  1. 替代方法_ASD干预:替代行为的正确使用方法和注意事项
  2. java实验四云南大学_云南大学JAVA程序设计实验四
  3. laravel报错:TokenMismatchException in VerifyCsrfToken.php
  4. docker日志位置
  5. python【力扣LeetCode算法题库】—两数之和
  6. C++ with STL(一)
  7. php error 关闭,php error_reporting()关闭报错
  8. 材料成型计算机模拟第三版,材料成型计算机模拟考试复习资料.doc
  9. .exp文件_mini_httpd 任意文件读取漏洞(附EXP脚本)
  10. 如何规划网站设计方案让用户访问更加舒适?