为了让我的panGesture(附加到UIImageView
)被调用,我需要确保用户不在图像所在的水平ScrollView
内滚动。我这样做是这样的:
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan(_:)))
panGesture.require(toFail: self.scrollView.panGestureRecognizer)
这很有效,但由于scrollView
仅水平,它只允许panGesture水平工作。我想垂直拉UIImage,但没有任何反应,因为很明显,只有在滚动视图失败时才能调用它(只能水平失败)。
有没有办法确保上述方法有效,但也允许panGesture
被称为垂直,无论如何?
答案 0 :(得分:2)
好的,我结合了几件事解决了这个问题。它看起来很多,但它会在其他项目中派上用场。
第1步:在水平和垂直方向启用水平滚动UIScrollView
“失败”。
虽然我只希望我的UIScrollView
水平滚动,但仍需要它垂直滚动,以便失败(解释即将发生)。失败使我能够“拉出”UIScrollView
的 out 。
scrollView
本身的高度为40
,因此它的contentSize
必须具有更大的高度,才能几乎垂直滚动。
self.effectTotalHeight.constant = 41
self.scrollView.contentSize = CGSize(width: contentWidth, height: self.effectTotalHeight.constant)
大!它垂直滚动。但现在内容橡皮筋(我们不想要)。反对其他人所说的,不要便宜,只是禁用反弹。 (特别是如果你想在水平滚动时弹跳!)
注意:我也意识到只在StoryBoard中禁用Bounce Horizontally
确实......好吧,没有(bug?)。
第2步:将UIScrollViewDelegate
添加到View Controller类并检测滚动
现在我想检测滚动并确保在垂直滚动时,实际滚动。为此,即使您正在滚动,contentOffset.y
位置也不应更改。 UIScrollViewDelegate
提供了一个在滚动时调用的委托函数scrollViewDidScroll
。只需将其设置为:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
scrollView.contentOffset.y = 0.0
}
为了调用此功能,您还需要将scrollView
的代理设置为self
:
self.scrollView.delegate = self
请注意,此控制所有 UIScrollView
,因此如果您希望它仅影响特定声明,请提供if
声明或switch
声明。在我的情况下,这个视图控制器只有一个UIScrollView
所以我没有放任何东西。
耶!所以现在它只是再次水平滚动,但这种方法只将contentOffset.y
保持在0
。它不会失败。我们确实需要它,因为scrollView
垂直失败是启用平移手势识别器的关键(什么让你拉动和拖动等)。所以让它失败吧!
第3步:覆盖UIScrollView
默认手势识别
为了覆盖任何默认手势识别器,我们需要将UIGestureRecognizerDelegate
作为另一个委托方法添加到View Controller类。
scrollView
现在需要自己的平移手势识别器处理程序,以便我们可以检测其上的手势。您还需要将新scrollGesture
的代理设置为self
,以便我们检测到它:
let scrollGesture = UIPanGestureRecognizer(target: self, action: #selector(self.handleScroll(_:)))
scrollGesture.delegate = self
self.scrollView.addGestureRecognizer(scrollGesture)
设置handleScroll
功能:
func handleScroll(_ recognizer: UIPanGestureRecognizer) {
// code here
}
这一切都很好,但为什么我们这样做呢?请记住,我们禁用了contentOffset.y,但垂直滚动不是失败。现在我们需要检测滚动的方向,如果垂直则让它失败,以便panHandle
可以被激活。
第4步:检测手势方向并让它失败(失败是好的!)
我扩展了UIPanGestureRecognizer
,以便通过进行以下公开扩展来检测和发出指示:
public enum Direction: Int {
case Up
case Down
case Left
case Right
public var isX: Bool { return self == .Left || self == .Right }
public var isY: Bool { return !isX }
}
public extension UIPanGestureRecognizer {
public var direction: Direction? {
let velo = velocity(in: view)
let vertical = fabs(velo.y) > fabs(velo.x)
switch (vertical, velo.x, velo.y) {
case (true, _, let y) where y < 0: return .Up
case (true, _, let y) where y > 0: return .Down
case (false, let x, _) where x > 0: return .Right
case (false, let x, _) where x < 0: return .Left
default: return nil
}
}
}
现在,为了正确使用它,您可以在recognizer
函数中获取.direction
的{{1}}并检测发出的方向。在这种情况下,我正在寻找handleScroll
或.Up
,我希望发出失败。我们通过禁用识别器来完成此操作,但之后立即重新启用它:
.Down
在func handleScroll(_ recognizer: UIPanGestureRecognizer) {
if (recognizer.direction == .Up || recognizer.direction == .Down) {
recognizer.isEnabled = false
recognizer.isEnabled = true
}
}
之后.isEnabled
被立即设置为true
的原因是false
将发出失败,启用另一个动作( “拉出”(平移)其内部视图),但不要永远禁用(否则它将停止被调用)。通过将其设置回false
,可以在发出故障后立即重新启用此侦听器。
第5步:让多个手势相互覆盖
最后但并非最不重要的是,这是一个非常非常重要的步骤,因为它允许平移手势和滚动手势独立工作,而不是只有一个总是覆盖另一个。
true
那就是那个!这是关于SO的大量研究(主要是找不起作用的东西)和实验,但它们都聚集在一起。
这是为了期望您已经编写了滚动视图中的对象的平移手势识别器,您正在“拉出”以及如何处理其状态(func gestureRecognizer(_: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWith shouldRecognizeSimultaneouslyWithGestureRecognizer:UIGestureRecognizer) -> Bool {
return true
}
,{{1}等)。我建议如果您需要帮助,请搜索SO。有很多答案。
如果您对此答案有任何其他疑问,请告知我们。