将垂直的两指滑动手势添加到UIScrollView

时间:2018-10-20 16:56:32

标签: ios swift

根据How to add a vertical swipe gesture to iPhone app for all screens,我向window添加了两根手指向下滑动手势,该手势在整个应用中的正常页面上都可以正常运行。但是它在具有UIScrollView的页面上失败(如UITableViewController)。当我用两根手指在UIScrollView上向下滑动时,它只是正常滚动。如果我从UINavigationBar上方的UIScrollView滑动,它又可以正常工作。

理想的结果是,我可以正常地用一根手指滚动表格视图,并通过用两根手指滑动页面而不滚动表格视图来调用某种方法。这在TweetBot中用于切换暗模式,效果很好。

根据Apple的文档:Using Responders and the Responder Chain to Handle Events,我想我了解响应程序链的工作原理,因此我想请UIScrollView忽略两个手指的滑动手势,以便它可以将此事件传递给{ {1}}。但我不知道如何:

我尝试从Apple的document实现UIWindow的{​​{1}},或者通过继承UIGestureRecognizerDelegate覆盖func gestureRecognizer(_:, shouldRequireFailureOf otherGestureRecognizer:)。但是一切都没有用。

欢迎任何解决方案或建议。

更新-最终解决方案

我简化了joern的解决方案,这里是

gestureRecognizerShouldBegin(_)

UITableView

AppDelegate

关于与joern解决方案不同之处的一些解释:

  1. 仅关注// It's not necessary to keep a reference to gesture unless you want to do something further. lazy var gesture: UIPanGestureRecognizer = { let gesture = UIPanGestureRecognizer(target: self, action: #selector(twoFingerDidSwipe(recognizer:))) gesture.minimumNumberOfTouches = 2 gesture.maximumNumberOfTouches = 2 return gesture }() func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { gesture.delegate = AppDelegate window.addGestureRecognizer(gesture) return true } @objc func twoFingerDidSwipe(recognizer: UIPanGestureRecognizer) { let swipeThreshold: CGFloat = 50 if recognizer.state == .changed { // 1 switch recognizer.translation(in: window).y { // 2 case ...(-swipeThreshold): print("Swipe Up") recognizer.state = .cancelled // 3 case swipeThreshold...: print("Swipe Down") recognizer.state = .cancelled default: break } } }
  2. extension AppDelegate: UIGestureRecognizerDelegate { func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true } } 的参数应该为recognizer.state == .change,这样我就无法对特定的VC进行任何操作。
  3. 通过此识别器触发某些方法后,应将其取消以防止持续触发它们。

而且不必对任何特定的VC做任何事情。

这样,两指滑动手势在普通VC和滚动VC上都可以正常工作。

1 个答案:

答案 0 :(得分:2)

UISwipeGestureRecognizer添加到窗口时,请保留对其的引用,以便以后可以通过AppDelegate对其进行访问:

class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    var twoFingerSwipeDownRecognizer: UISwipeGestureRecognizer?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        let twoFingerSwipeDownRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(didRecognizeTwoFingerSwipeDown))
        twoFingerSwipeDownRecognizer.numberOfTouchesRequired = 2
        twoFingerSwipeDownRecognizer.direction = .down
        window?.addGestureRecognizer(twoFingerSwipeDownRecognizer)
        self.twoFingerSwipeDownRecognizer = twoFingerSwipeDownRecognizer

        return true
    }

    @objc func didRecognizeTwoFingerSwipeDown(recognizer: UISwipeGestureRecognizer) {
        print("SWIPE DOWN")
    }
}

然后,在包含UIViewController(或UITableView)的UIScrollView中,您必须在UITableView的平移手势识别器上调用require(toFail:)

func enableTwoFingerSlideDown() {
    guard
        let appDelegate = UIApplication.shared.delegate as? AppDelegate,
        let twoFingerGestureRecognizer = appDelegate.twoFingerSwipeDownRecognizer
        else {
            return
        }
    tableView.panGestureRecognizer.require(toFail: twoFingerGestureRecognizer)
}

现在,两指向下的手势作用在UITableView上。

更新

由于滑动是一个离散手势,因此上述解决方案可能不是完美的解决方案。当您用一根手指缓慢向下滚动时,您会注意到UITableView不会立即向下滚动。延迟很短,因为UITableView's UIPanGestureDelagate必须等待,直到(两指)SwipeDelegate失败为止。这需要一些时间。

更好的解决方案可能是使用UIPanGestureRecognizer来识别两指平移,然后在用户使用两根手指进行平移时禁用UITableView上的滚动。

可以这样实现:

在您的AppDelegate中:

class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    var twoFingerPanRecognizer: UIPanGestureRecognizer?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        let twoFingerSwipeDownRecognizer = UIPanGestureRecognizer(target: self, action: #selector(didRecognizeTwoFingerPan))
        twoFingerSwipeDownRecognizer.minimumNumberOfTouches = 2
        twoFingerSwipeDownRecognizer.maximumNumberOfTouches = 2
        twoFingerPanRecognizer?.delegate = self
        window?.addGestureRecognizer(twoFingerSwipeDownRecognizer)
        self.twoFingerPanRecognizer = twoFingerSwipeDownRecognizer

        return true
    }

    @objc func didRecognizeTwoFingerPan(recognizer: UIPanGestureRecognizer) {
        let tableView = recognizer.view as? UITableView
        switch recognizer.state {
        case .began:
            tableView?.isScrollEnabled = false
        case .changed:
            let swipeThreshold: CGFloat = 50
            switch recognizer.translation(in: nil).y {
            case ...(-swipeThreshold):
                print("Swipe UP")
                recognizer.isEnabled = false
            case swipeThreshold...:
                print("Swipe DOWN")
                recognizer.isEnabled = false
            default:
                break
            }
        case .cancelled, .ended, .failed, .possible:
            tableView?.isScrollEnabled = true
            recognizer.isEnabled = true
        }
    }
}

extension AppDelegate: UIGestureRecognizerDelegate {
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
}

在您的ViewController中:

func enableTwoFingerSlideDown() {
        guard
            let appDelegate = UIApplication.shared.delegate as? AppDelegate,
            let twoFingerGestureRecognizer = appDelegate.twoFingerPanRecognizer
            else {
                return
            }
        tableView.addGestureRecognizer(twoFingerGestureRecognizer)
    }

您必须从UIPanGestureRecognizer中取出AppDelegate并将其添加到UITableView中。否则,这将无法工作。只需记住,在撤消此UIWindow之前,将其添加回UIViewController

使用此解决方案,UITableView的正常滚动行为保持不变。