将垂直的两指滑动手势添加到UIScrollView [英] Add vertical two finger swipe gesture to UIScrollView

查看:62
本文介绍了将垂直的两指滑动手势添加到UIScrollView的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据如何要为iPhone屏幕上的所有屏幕添加垂直滑动手势,我向 window 中添加了两根手指向下滑动手势,在整个应用中的正常页面上都可以正常工作.但是它在具有 UIScrollView 的页面上失败(如UITableViewController).当我用两根手指在 UIScrollView 上向下滑动时,它会像往常一样滚动.如果我从 UIScrollView 上方的 UINavigationBar 滑动,它再次可以正常工作.

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

根据Apple的文档:使用响应者和响应者链来处理事件,我想我了解了响应程序链的工作原理,所以我想让 UIScrollView 忽略两个手指的滑动手势,以便它可以将此事件传递给 UIWindow .但我不知道如何:

我尝试从Apple的

还有

 扩展名AppDelegate:UIGestureRecognizerDelegate {funcgestureRecognizer(_gestureRecognizer:UIGestureRecognizer,应该与其他GestureRecognizer同时识别:UIGestureRecognizer)->布尔{返回真}} 

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

  1. 仅涉及 recognizer.state == .change .
  2. recognizer.translation(in:)的参数应为 window ,这样我就无法对特定的VC进行任何操作.
  3. 此识别器触发某些方法后,应将其取消以防止连续触发它们.

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

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

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

 类AppDelegate:UIResponder,UIApplicationDelegate {var window:UIWindow?var twoFingerSwipeDownRecognizer:UISwipeGestureRecognizer吗?func application(_ application:UIApplication,didFinishLaunchingWithOptions launchOptions:[UIApplication.LaunchOptionsKey:Any]?)->布尔{让twoFingerSwipeDownRecognizer = UISwipeGestureRecognizer(target:self,action:#selector(didRecognizeTwoFingerSwipeDown))twoFingerSwipeDownRecognizer.numberOfTouchesRequired = 2twoFingerSwipeDownRecognizer.direction = .downwindow?.addGestureRecognizer(twoFingerSwipeDownRecognizer)self.twoFingerSwipeDownRecognizer = twoFingerSwipeDownRecognizer返回真}@objc func didRecognizeTwoFingerSwipeDown(recognizer:UISwipeGestureRecognizer){打印(向下滑动")}} 

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

  func enableTwoFingerSlideDown(){警卫让appDelegate = UIApplication.shared.delegate为?AppDelegate,让twoFingerGestureRecognizer = appDelegate.twoFingerSwipeDownRecognizer别的 {返回}tableView.panGestureRecognizer.require(toFail:twoFingerGestureRecognizer)} 

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

更新

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

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

可以这样实现:

在您的AppDelegate中:

 类AppDelegate:UIResponder,UIApplicationDelegate {var window:UIWindow?var twoFingerPanRecognizer:UIPanGestureRecognizer?func application(_ application:UIApplication,didFinishLaunchingWithOptions launchOptions:[UIApplication.LaunchOptionsKey:Any]?)->布尔{让twoFingerSwipeDownRecognizer = UIPanGestureRecognizer(target:self,action:#selector(didRecognizeTwoFingerPan))twoFingerSwipeDownRecognizer.minimumNumberOfTouches = 2twoFingerSwipeDownRecognizer.maximumNumberOfTouches = 2twoFingerPanRecognizer?.delegate =自我window?.addGestureRecognizer(twoFingerSwipeDownRecognizer)self.twoFingerPanRecognizer = twoFingerSwipeDownRecognizer返回真}@objc func didRecognizeTwoFingerPan(recognizer:UIPanGestureRecognizer){让tableView = ogniser.view为?UITableView切换识别器状态{案例.开始:tableView?.isScrollEnabled = false大小写已更改:让swipeThreshold:CGFloat = 50切换识别器.translation(in:nil).y {情况...(-swipeThreshold):打印(向上滑动")identifierr.isEnabled =否案例swipeThreshold ...:打印(向下滑动")identifierr.isEnabled =否默认:休息}.cancelled,.end,.failed,.possible的情况:tableView?.isScrollEnabled = true识别器.isEnabled = true}}}扩展AppDelegate:UIGestureRecognizerDelegate {funcgestureRecognizer(_gestureRecognizer:UIGestureRecognizer,应该与其他GestureRecognizer同时识别:UIGestureRecognizer)->布尔{返回真}} 

在您的ViewController中:

  func enableTwoFingerSlideDown(){警卫让appDelegate = UIApplication.shared.delegate为?AppDelegate,让twoFingerGestureRecognizer = appDelegate.twoFingerPanRecognizer别的 {返回}tableView.addGestureRecognizer(twoFingerGestureRecognizer)} 

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

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

According to How to add a vertical swipe gesture to iPhone app for all screens, I add a two finger swipe down gesture to window, which works fine on normal pages throughout the whole app. But it fails on the page has UIScrollView (like UITableViewController). When I swipe down with two fingers on UIScrollView, it just scrolls it as normal. If I swipe from UINavigationBar that above UIScrollView, it works fine again.

The ideal result is that I can scroll tableview by one finger normally, and call some method by swiping page with two fingers without scrolling tableview. This is used in TweetBot to switch dark mode, works perfectly.

According to Apple's document: Using Responders and the Responder Chain to Handle Events, I think I understand how the Responder Chain works, so I want to ask UIScrollView to ignore the two fingers swipe gesture so that it can pass this event to UIWindow. But I can't figure out how to:

I tried to implement UIGestureRecognizerDelegate's func gestureRecognizer(_:, shouldRequireFailureOf otherGestureRecognizer:) from Apple's document, or override gestureRecognizerShouldBegin(_) by inherit UITableView. But all didn't work.

Any solutions or advice is welcomed.

Update - Final Solution

I simplified joern's solution and here is.

In AppDelegate

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

And

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

Some explanations about differences from joern's solution:

  1. Only recognizer.state == .change is concerned.
  2. recognizer.translation(in:)'s parameter should be window so that I can do nothing about particular VCs.
  3. After triggering some methods by this recognizer, it should be cancelled to prevent from triggering them continually.

And It's unnecessary to do anything about any particular VC.

With this, two-finger swipe gesture works fine on both normal VC and scroll VC.

解决方案

When you add the UISwipeGestureRecognizer to your window, keep a reference to it, so you can access it later via the 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")
    }
}

Then, in the UIViewController that contains the UITableView (or UIScrollView) you have to call require(toFail:) on the UITableView's pan gesture recognizer:

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

Now the two finger down gesture works over a UITableView.

UPDATE

Because a swipe is a discrete gesture the solution above might not be the perfect solution. When you scroll down slowly with one finger you will notice that the UITableView will not scroll down instantly. There is a short delay because the UITableView's UIPanGestureDelagate has to wait until the (Two Finger) SwipeDelegate has failed. And that takes some time.

A better solution might be to use a UIPanGestureRecognizer to recognize a two finger pan and then disable the scrolling on the UITableView while the user is panning using two fingers.

That could be achieved like this:

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

In your ViewController:

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

You have to take the UIPanGestureRecognizer from the AppDelegate and add it to the UITableView. Otherwise this won't work. Just remember to add it back to the UIWindow before this UIViewController is dismissed.

With this solution the normal scrolling behavior of the UITableView remains unchanged.

这篇关于将垂直的两指滑动手势添加到UIScrollView的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆