根据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解决方案不同之处的一些解释:
// 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
}
}
}
。extension AppDelegate: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
的参数应该为recognizer.state == .change
,这样我就无法对特定的VC进行任何操作。而且不必对任何特定的VC做任何事情。
这样,两指滑动手势在普通VC和滚动VC上都可以正常工作。
答案 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
的正常滚动行为保持不变。