Как я могу реализовать "перетащить право, чтобы отклонить" контроллер просмотра, который находится в стеке навигации?

По умолчанию, если вы перетаскиваете правую сторону от левого края экрана, он будет перетаскивать ViewController и снимать его со стека.

Я хочу расширить эту функциональность до всего экрана. Когда пользователь тянет куда угодно, мне бы хотелось, чтобы это произошло.

Я знаю, что я могу реализовать правый жест прокрутки и просто вызвать self.navigationController?.popViewControllerAnimated(true)

Тем не менее, нет "перетаскивания" движения. Я хочу, чтобы пользователь мог перетащить контроллер вида прямо, как если бы это был объект, который показывает, что внизу. И, если он перетащит 50%, отпустите его. (Посмотрите, что я имею в виду.)

Ответ 1

введите описание изображения здесь

Сделал демонстрационный проект в Github
https://github.com/rishi420/SwipeRightToPopController

Я использовал протокол UIViewControllerAnimatedTransitioning

Из документа:

//Это используется для процентных интерактивных переходов, а также для контейнерных контроллеров...

Добавлено представление UIPanGestureRecognizer в виде контроллера. Это действие жеста:

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. делегат будет необходим для InteractiveTransitioning
  • .Changed: UpdateInteractiveTransition с процентом
  • .Ended: Продолжить оставшееся переключение, если перетащить 50% или более или больше скорость еще отменить
  • .Cancelled, .Failed: отменить переход


Ссылки:

Ответ 2

Swift 4 версия принятого ответа от @Warif Akhand Rishi

Хотя этот ответ работает, есть две причуды, которые я узнал об этом.

  1. Если вы проведете пальцем влево, он также исчезнет, как если бы вы двигались вправо.
  2. это также очень деликатно, потому что, если даже небольшое движение направлено в любом направлении, оно будет отклонять 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
            navigationController?.popViewController(animated: true)

        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
        }
    }
}

Ответ 4

Создайте распознаватель жестов панорамирования и переместите цели интерактивного распознавателя поп-жестов.

Добавьте свой распознаватель в контроллер push-view viewDidLoad и вуаля!

let popGestureRecognizer = self.navigationController!.interactivePopGestureRecognizer!
if let targets = popGestureRecognizer.value(forKey: "targets") as? NSMutableArray {
  let gestureRecognizer = UIPanGestureRecognizer()
  gestureRecognizer.setValue(targets, forKey: "targets")
  self.view.addGestureRecognizer(gestureRecognizer)
}

Ответ 5

Проведите пальцем вправо, чтобы закрыть диспетчер вида

Версия 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)
                })
            }
        }
    }
}

Надеюсь, это поможет. Не стесняйтесь предлагать изменения в случае необходимости.

Ответ 6

С этой целью вы можете использовать жестов для прокрутки:

override func viewDidLoad() {
    super.viewDidLoad()

    var swipeRight = UISwipeGestureRecognizer(target: self, action: "popViewController:")
    swipeRight.direction = UISwipeGestureRecognizerDirection.Right
    self.view.addGestureRecognizer(swipeRight)
}


func popViewController(gesture: UIGestureRecognizer) {

    if let swipeGesture = gesture as? UISwipeGestureRecognizer {
        switch swipeGesture.direction {
            case UISwipeGestureRecognizerDirection.Right:
               self.navigationController.popViewControllerAnimated(true)
            default:
                break
        }
    }
}