Ios “我如何实施?”;拖动鼠标右键可解除“锁定”;一个视图控制器';导航堆栈中有什么?

Ios “我如何实施?”;拖动鼠标右键可解除“锁定”;一个视图控制器';导航堆栈中有什么?,ios,swift,Ios,Swift,默认情况下,如果从屏幕左边缘向右拖动,它将拖走ViewController并将其从堆栈中移除 我想将此功能扩展到整个屏幕。当用户拖动到任何地方时,我希望同样的情况发生 我知道我可以实现正确的滑动手势,只需调用self.navigationController?.popViewControllerAnimated(true) 但是,不存在“拖动”运动。我希望用户能够右键拖动视图控制器,就像它是一个对象一样,显示下面的内容。而且,如果超过50%,就驳回它。(请查看instagram,了解我的意思。)

默认情况下,如果从屏幕左边缘向右拖动,它将拖走ViewController并将其从堆栈中移除

我想将此功能扩展到整个屏幕。当用户拖动到任何地方时,我希望同样的情况发生

我知道我可以实现正确的滑动手势,只需调用
self.navigationController?.popViewControllerAnimated(true)

但是,不存在“拖动”运动。我希望用户能够右键拖动视图控制器,就像它是一个对象一样,显示下面的内容。而且,如果超过50%,就驳回它。(请查看instagram,了解我的意思。)

在Github中制作了一个演示项目

我使用了
UIViewControllerAnimatedTransitioning
协议

从文档:

//这用于百分比驱动的交互式转换,以及容器控制器

在控制器视图中添加了一个
UIPangestureRecognitor
。这是手势的动作:

func handlePanGesture(panGesture: UIPanGestureRecognizer) {

    let percent = max(panGesture.translationInView(view).x, 0) / view.frame.width

    switch panGesture.state {

    case .Began:
        navigationController?.delegate = self
        navigationController?.popViewControllerAnimated(true)

    case .Changed:
        percentDrivenInteractiveTransition.updateInteractiveTransition(percent)

    case .Ended:
        let velocity = panGesture.velocityInView(view).x

        // Continue if drag more than 50% of screen width or velocity is higher than 1000
        if percent > 0.5 || velocity > 1000 {
            percentDrivenInteractiveTransition.finishInteractiveTransition()
        } else {
            percentDrivenInteractiveTransition.cancelInteractiveTransition()
        }

    case .Cancelled, .Failed:
        percentDrivenInteractiveTransition.cancelInteractiveTransition()

    default:
        break
    }
}
步骤:

  • 计算视图上的拖动百分比
  • .Begin:
    指定要执行和分配的序列
    UINavigationController
    委派。
    交互翻译需要委托人
  • .Changed:
    使用百分比更新InteractiveTransition
  • .end:
    如果拖动速度大于等于50%或更高,则继续剩余的过渡,否则取消
  • .Cancelled.。失败:
    取消转换

  • 参考资料:


  • 您需要调查
    UINavigationController
    的属性

    下面是一个类似的问题,用示例代码将其连接起来


    Swift 4版本@Warif Akhand Rishi接受的答案

    尽管这个答案确实有效,但我发现了两个怪癖

  • 如果你向左滑动,它也会像你向右滑动一样关闭
  • 这也是非常微妙的,因为如果哪怕是朝任何一个方向轻扫,它都会将vc拒之门外
  • 除此之外,它肯定是有效的,您可以向右或向左滑动以解除锁定

    class ViewController: UIGestureRecognizerDelegate, UINavigationControllerDelegate {
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            navigationController?.interactivePopGestureRecognizer?.delegate = self
            let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:)))
            view.addGestureRecognizer(panGesture)
        }
    
        @objc func handlePanGesture(_ gesture: UIPanGestureRecognizer){
    
            let interactiveTransition = UIPercentDrivenInteractiveTransition()
    
            let percent = max(gesture.translation(in: view).x, 0) / view.frame.width
    
            switch gesture.state {
    
            case .began:
                navigationController?.delegate = self
    
                // *** use this if the vc is PUSHED on the stack **
                navigationController?.popViewController(animated: true)
    
                // *** use this if the vc is PRESENTED **
                //navigationController?.dismiss(animated: true, completion: nil)
    
            case .changed:
                interactiveTransition.update(percent)
    
            case .ended:
                let velocity = gesture.velocity(in: view).x
    
                // Continue if drag more than 50% of screen width or velocity is higher than 1000
                if percent > 0.5 || velocity > 1000 {
                    interactiveTransition.finish()
                } else {
                    interactiveTransition.cancel()
                }
    
            case .cancelled, .failed:
                interactiveTransition.cancel()
    
            default:break
            }
        }
    }
    

    向右滑动以关闭视图控制器

    Swift 5版本- (从右向左滑动时,也删除了手势识别)

    重要- 在VC2的“属性检查器”中,将“演示”值从“全屏”设置为“全屏”。这将允许VC1在通过手势解除VC2时可见 — 没有它,VC2后面将出现黑屏,而不是VC1

    class ViewController: UIGestureRecognizerDelegate, UINavigationControllerDelegate {
    
        var initialTouchPoint: CGPoint = CGPoint(x: 0, y: 0)
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            navigationController?.interactivePopGestureRecognizer?.delegate = self
            let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:)))
            view.addGestureRecognizer(panGesture)
        }
    
        @objc func handlePanGesture(_ sender: UIPanGestureRecognizer) {
            let touchPoint = sender.location(in: self.view?.window)
            let percent = max(sender.translation(in: view).x, 0) / view.frame.width
            let velocity = sender.velocity(in: view).x
    
            if sender.state == UIGestureRecognizer.State.began {
                initialTouchPoint = touchPoint
            } else if sender.state == UIGestureRecognizer.State.changed {
                if touchPoint.x - initialTouchPoint.x > 0 {
                    self.view.frame = CGRect(x: touchPoint.x - initialTouchPoint.x, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height)
                }
            } else if sender.state == UIGestureRecognizer.State.ended || sender.state == UIGestureRecognizer.State.cancelled {
    
                if percent > 0.5 || velocity > 1000 {
                    navigationController?.popViewController(animated: true)
                } else {
                    UIView.animate(withDuration: 0.3, animations: {
                        self.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height)
                    })
                }
            }
        }
    }
    
    希望这有帮助。
    如有必要,请随时提出更改建议。

    创建平移手势识别器,并将交互式pop手势识别器的目标移动到不同的位置

    将您的识别器添加到推送视图控制器的viewDidLoad中,瞧

    编辑:用更详细的解决方案更新了代码

    导入操作系统
    导入UIKit
    公共扩展UINavigationController{
    func FixInteractiveProgTestureRecognitor(委托:UIGestureRecognitzerDelegate){
    警卫
    让popGestureRecognizer=interactivePopGestureRecognizer,
    将targets=popGestureRecognitor.value(forKey:“targets”)设为?NSMutableArray,
    让gestureRecognizers=view.gestureRecognizers,
    //swiftlint:禁用空\u计数
    目标计数>0
    else{return}
    如果viewControllers.count==1{
    对于手势识别器中的识别器,其中识别器为泛方向手势识别器{
    view.RemovegestureRecognitor(识别器)
    PopGestureRecognitor.isEnabled=false
    recognizer.delegate=nil
    }
    }否则{
    如果gestureRecognizers.count==1{
    让gestureRecognizer=PanDirectionGestureRecognizer(轴:。水平,方向:。右侧)
    gestureRecognizer.cancelsTouchesInView=false
    GestureRecognitor.setValue(目标,forKey:“目标”)
    手势识别器.require(toFail:popGestureRecognizer)
    gestureRecognizer.delegate=委托
    PopGestureRecognitor.isEnabled=true
    view.addGestureRecognizer(gestureRecognizer)
    }
    }
    }
    }
    
    公共枚举泛轴{
    垂直箱
    案例水平
    }
    公共枚举泛方向{
    案例左
    案例权利
    开箱
    结案
    正常情况
    }
    公共类PanDirectionGestureRecognitor:UIPangestureRecognitor{
    让轴:泛轴
    方向:泛方向
    public init(轴:泛轴,方向:泛方向=.normal,目标:AnyObject?=nil,操作:选择器?=nil){
    self.axis=轴
    方向
    super.init(目标:目标,操作:操作)
    }
    覆盖公共函数触摸移动(touch:Set,带有事件:UIEvent){
    super.touchesMoved(touches,with:event)
    如果状态==。开始{
    设vel=速度(在:视图中)
    开关轴{
    壳体水平,其中abs(y级)>abs(x级):
    状态=。已取消
    壳体垂直,其中abs(x级)>abs(y级):
    状态=。已取消
    违约:
    打破
    }
    设isIncrement=轴==水平?x级>0:y级>0
    开关方向{
    案例。左侧为增量:
    状态=。已取消
    案例。正确位置!i增加:
    状态=。已取消
    案例。增加的地方:
    状态=。已取消
    case.down where!i增量:
    状态=。已取消
    违约:
    打破
    }
    }
    }
    }
    
    例如,在集合视图中:

    open override func didMove(toParent父对象:UIViewController?){
    navigationController?.FixInteractiveProgTestureRecognitor(代理:self)
    }
    

    我认为这比建议的解决方案更简单,而且也适用于导航中的所有ViewController以及嵌套的scrollvi
    // MARK: - UIGestureRecognizerDelegate
    extension BaseCollection: UIGestureRecognizerDelegate {
      public func gestureRecognizer(
        _ gestureRecognizer: UIGestureRecognizer,
        shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer
      ) -> Bool {
        otherGestureRecognizer is PanDirectionGestureRecognizer
      }
    }