登录 立即注册
金钱:

Code4App-iOS开发-iOS 开源代码库-iOS代码实例搜索-iOS特效示例-iOS代码例子下载-Code4App.com

非常简单的让navigationVc可以全屏返回

[复制链接]
来自: zerojswift 分类: iOS精品源码 上传时间: 2016-6-30 20:31:43

项目介绍:

前言
其实, Apple已经提供了navigationController中的控制器都有一个从屏幕左边滑动pop的手势, 并且转换控制器之间的各种动画也是已经实现好了, 但是现在很多APP中都有全屏滑动返回的功能, 确实手机屏幕变大后在一定程度上使用是方便了很多. 暂且不管这种交互设计好还是不好, 既然这么多的APP(微博, QQ, 简书, 网易新闻...)中都在使用, 肯定在开发中实现这个功能也是必要的了.


首先展示一下最终的使用方法, 使用还是比较方便
  • 第一种, 使用提供的自定义的navigationController

    • 如果在storyboard中使用, 子需要将navigationController设置为自定义的即可, 默认拥有全屏滑动返回功能, 如果需要关闭, 在需要的地方设置如下即可// 设置为true的时候开启全屏滑动返回功能, 设置为false, 关闭  
      [Objective-C] 查看源文件 复制代码
      // 设置为true的时候开启全屏滑动返回功能, 设置为false, 关闭
         (navigationController as? CustomNavigationController)?.enabledFullScreenPop(isEnabled: false)


      1.png
    • storyboard中使用

    • 如果使用代码初始化, 那么直接使用自定义的navigationController初始化即可  

[Objective-C] 查看源文件 复制代码
  // 同样的默认是开启全屏滑动返回功能的
   let navi = CustomNavigationController(rootViewController: rootVc)
   //如果需要关闭或者重新开启, 在需要的地方使用下面方法
   (navigationController as? CustomNavigationController)?.enabledFullScreenPop(isEnabled: false)



  • 第二种, 使用提供的navigationController的分类
    这种方法, 并没有默认开启, 需要我们自己开启或者关闭全屏滑动返回功能     

[Objective-C] 查看源文件 复制代码
      // 在需要的地方, 获取到navigationController, 然后使用分类方法开启(关闭)全屏返回手势即可
      navigationController?.zj_enableFullScreenPop(isEnabled: true)





实现方法: 实现的方法很多, 比如可以利用系统提供的navigationController的手势方法, 利用运行时获取到这个手势的target和selector, 然后, 我们使用分类或者自定义navigationController在上面添加一个pan手势, 将这个手势的target和selector设置为运行时获取到系统手势的target和selector, 那么, 这个手势就拥有了和系统滑动返回相同的效果, 实现上还是很方便的
但是这里, 我想介绍的是另一种Apple推荐的自定义转场动画的方法,
关于自定义转场动画的各种知识, 如果你不是很熟悉, 介意大家看看我之前的
这篇文章介绍(当时写就是为了实现这篇文章铺垫), 里面介绍了详细的自定义教程, 不过利用是示例了present/dismiss的使用
  • 新建一个ZJNavigationControllerDelegate用于自定义的navigationController的delegate
    [Objective-C] 查看源文件 复制代码
    class ZJNavigationControllerDelegate: NSObject, UINavigationControllerDelegate {
     let animator = ZJNavigationControllerAnimator()
     let interactive = ZJNavigationControllerInteractiveTransition()
     var panGesture: UIPanGestureRecognizer! = nil {
         didSet {
             interactive.panGesture = panGesture
         }
     }
    
     func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
         interactive.navigationController = navigationController
    
         animator.operation = operation
         return animator
     }
     // 这里是手势交互动画需要的对象
     func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
         return interactive.isInteracting ? interactive : nil
     }
    //    deinit {
    //        print("\(self.debugDescription) --- 销毁")
    //    }
    }


  • 新建一个ZJNavigationControllerAnimator继承自NSObject,并实现UIViewControllerAnimatedTransitioning协议, 来实现具体的动画
    [Objective-C] 查看源文件 复制代码
    class ZJNavigationControllerAnimator: NSObject, UIViewControllerAnimatedTransitioning {
      let duration = 0.35
      var operation: UINavigationControllerOperation = .none
      func transitionDuration(_ transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
          return duration
      }
      func animateTransition(_ transitionContext: UIViewControllerContextTransitioning) {
          // fromVc 总是获取到正在显示在屏幕上的Controller
          let fromVc = transitionContext.viewController(forKey: UITransitionContextFromViewControllerKey)!
          // toVc 总是获取到将要显示的controller
          let toVc = transitionContext.viewController(forKey: UITransitionContextToViewControllerKey)!
    
          let containView = transitionContext.containerView()
    
          let toView: UIView
          let fromView: UIView
          // Animators should not directly manipulate a view controller's views and should
          // use viewForKey: to get views instead.
          if transitionContext.responds(to:NSSelectorFromString("viewForKey:")) {
              // 通过这种方法获取到view不一定是对应controller.view
              toView = transitionContext.view(forKey: UITransitionContextToViewKey)!
              fromView = transitionContext.view(forKey: UITransitionContextFromViewKey)!
          } else {
              toView = toVc.view
              fromView = fromVc.view
          }
          // 最终显示在屏幕上的controller的frame
          let visibleFrame = transitionContext.initialFrame(for: fromVc)
          // 隐藏在右边的controller的frame
          let rightHiddenFrame = CGRect(origin: CGPoint(x: visibleFrame.width, y: visibleFrame.origin.y) , size: visibleFrame.size)
          // 隐藏在左边的controller的frame
          let leftHiddenFrame = CGRect(origin: CGPoint(x: -visibleFrame.width/2, y: visibleFrame.origin.y) , size: visibleFrame.size)
          if operation == .push {// push
              toView.frame = rightHiddenFrame
              fromView.frame = visibleFrame
              //  添加toview到最上面(fromView是当前显示在屏幕上的view不用添加)
              containView.addSubview(toView)
          } else {// pop
              fromView.frame = visibleFrame
              toView.frame = leftHiddenFrame
              // 有时需要将toView添加到fromView的下面便于执行动画
              containView.insertSubview(toView, belowSubview: fromView)
          }
          UIView.animate(withDuration: duration, delay: 0.0, options: [.curveLinear], animations: {
              if self.operation == .push {
                  toView.frame = visibleFrame
                  fromView.frame = leftHiddenFrame
              } else {
                  fromView.frame = rightHiddenFrame
                  toView.frame = visibleFrame
              }
          }) { (_) in
              let cancelled = transitionContext.transitionWasCancelled()
              if cancelled {
                  // 如果中途取消了就移除toView(可交互的时候会发生)
                  toView.removeFromSuperview()
              }
              // 通知系统动画是否完成或者取消了(必须)
              transitionContext.completeTransition(!cancelled)
          }
      }
    //    deinit {
    //        print("\(self.debugDescription) --- 销毁")
    //    }
    }



  • 新建一个ZJNavigationControllerInteractiveTransition继承自
    UIPercentDrivenInteractiveTransition, 来处理手势的过程

    [Objective-C] 查看源文件 复制代码
    class ZJNavigationControllerInteractiveTransition: UIPercentDrivenInteractiveTransition {
      var panGesture: UIPanGestureRecognizer! = nil {
          didSet {
              panGesture.addTarget(self, action: #selector(self.handlePan(gesture:)))
          }
      }
      var containerView: UIView!
      var navigationController: UINavigationController! = nil {
          didSet {
              containerView = navigationController.view
              containerView.addGestureRecognizer(panGesture)
          }
      }
      var isInteracting = false
    
      override init() {
          super.init()
      }
      func handlePan(gesture: UIPanGestureRecognizer) {
    
          func finishOrCancel() {
              let translation = gesture.translation(in: containerView)
              let percent = translation.x / containerView.bounds.width
              let velocityX = gesture.velocity(in: containerView).x
              let isFinished: Bool
    
              // 修改这里可以改变手势结束时的处理
              if velocityX > 100 {
                  isFinished = true
              } else if percent > 0.5 {
                  isFinished = true
              } else {
                  isFinished = false
              }
              isFinished ? finish() : cancel()
          }
          switch gesture.state {
          case .began:
              isInteracting = true
              // pop
              if navigationController.viewControllers.count > 0 {
    
                  _ = navigationController.popViewController(animated: true)
              }
          case .changed:
              if isInteracting {
                  let translation = gesture.translation(in: containerView)
                  var percent = translation.x / containerView.bounds.width
                  percent = max(percent, 0)
                  update(percent)
              }
          case .cancelled:
              if isInteracting {
                  finishOrCancel()
                  isInteracting = false
              }
          case .ended:
              if isInteracting {
                  finishOrCancel()
                  isInteracting = false
              }
          default:
              break
          }
      }
    }



  • 最后自定义navigationController
    [Objective-C] 查看源文件 复制代码
    class CustomNavigationController: UINavigationController {
    
      private(set) var panGesture: UIPanGestureRecognizer?
      private var customDelegate: CustomNavigationControllerDelegate?
    
      required init?(coder aDecoder: NSCoder) {
          super.init(coder: aDecoder)
          enabledFullScreenPop(isEnabled: true)
      }
    
      override init(rootViewController: UIViewController) {
          super.init(rootViewController: rootViewController)
          enabledFullScreenPop(isEnabled: true)
      }
    
      init() {
          super.init(nibName: nil, bundle: nil)
          enabledFullScreenPop(isEnabled: true)
      }
    
      // 开启或者关闭全屏pop手势(默认开启)
      func enabledFullScreenPop(isEnabled: Bool) {
          if isEnabled {
              if customDelegate == nil {
                  // 创建代理对象
                  customDelegate = CustomNavigationControllerDelegate()
                  // 创建手势
                  panGesture = UIPanGestureRecognizer()
                  // 传递手势给代理
                  customDelegate?.panGesture = panGesture
                  // 设置代理为自定义的
                  delegate = customDelegate
              }
          } else {
              customDelegate = nil
              panGesture = nil
              delegate = nil
          }
      }
    }




到这里, 实现的全部过程就完成了, 当然了, 这里并没有处理控制器中如果有scrollView的时候的可能的手势冲突, 大家可以自己去尝试处理一下

DEMO下载:


文/ZeroJ(简书作者)


相关源码推荐:

我来说两句
*滑动验证:
所有评论(61)
AlonMessi 2016-7-1 11:07:06
感谢大神分享,必须顶!
回复
BlueManlove 2016-7-1 11:10:02
code4app好的代码demo真的很多,谢谢啦~
回复
phoiu 2016-7-1 11:11:12
内容很好,棒棒哒
回复
hellokenken 2016-7-1 11:12:25
code4app好的代码demo真的很多,谢谢啦~
回复
littleRed 2016-7-1 11:14:18
感谢大神分享,必须顶!
回复
kengsir 2016-7-1 11:18:10
支持,感谢,祝code4app越来越好~
回复
dfsgsdfgsdfgdg 2016-7-1 16:39:25
还可以吧
回复
scaf0664 2016-7-1 18:14:28
虽不明,但觉厉!
回复
scaf0664 2016-7-1 18:14:43
精华内容,楼主V5!
回复
提取码:  下载次数:38 状态:已购或VIP 售价:0(原价:10)金钱 下载权限:初级码农 
2062 0 38
联系我们
首页/微信公众账号投稿

帖子代码编辑/版权问题

QQ:435399051,742864542

如何获得代码达人称号?

代码贡献英雄榜
用户名 下载数
通过邮件订阅最新 Code4App 信息
上一条 /4 下一条
联系我们
关闭
合作电话:
13802416937
Email:
435399051@qq.com
商务市场合作/投稿
问题反馈及帮助
联系我们

广告投放| Github|申请友链|手机版|Code4App ( 粤ICP备15117877号-1 )

快速回复 返回顶部 返回列表