同一视图上的多个手势(从可滚动视图中拖出视图)无法正常工作?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了同一视图上的多个手势(从可滚动视图中拖出视图)无法正常工作?相关的知识,希望对你有一定的参考价值。

为了让我的panGesture(附加到UIImageView)被调用,我需要确保用户不在图像所在的水平ScrollView中滚动。我这样做是这样的:

let panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan(_:)))
    panGesture.require(toFail: self.scrollView.panGestureRecognizer)

这很好,但由于scrollView只是水平的,它只允许panGesture水平工作。我还希望垂直拉动UIImage,但没有任何反应,因为很明显,只有在滚动视图失败时才会调用它(只能水平失败)。

有没有办法确保上述工作,但也允许panGesture垂直调用,无论如何?

答案

好的,我结合了几件事解决了这个问题。它看起来很多,但它会在其他项目中派上用场。

步骤1:水平和垂直启用水平滚动UIScrollView“失败”。

虽然我只想让我的UIScrollView水平滚动,我仍然需要它垂直滚动,以便它可以失败(解释即将到来)。失败使我能够从UIScrollView“拉出”子视图。

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

请记住,这将控制所有UIScrollViews,因此如果您希望它只影响特定的声明,请提供if声明或switch声明。在我的情况下,这个视图控制器只有一个UIScrollView所以我没有放任何东西。

好极了!所以现在它只是水平滚动,但这种方法只保持contentOffset.y0。它不会让它失败。我们确实需要它,因为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函数中获得.directionhandleScroll并检测发射的方向。在这种情况下,我正在寻找.Up.Down,我希望它发出一个失败。我们通过禁用识别器来完成此操作,但之后立即重新启用它:

func handleScroll(_ recognizer: UIPanGestureRecognizer) {
    if (recognizer.direction == .Up || recognizer.direction == .Down) {
        recognizer.isEnabled = false
        recognizer.isEnabled = true
    }
}

.isEnabledtrue之后立即设置为false的原因是因为false将发出失败,启用另一个手势(“拉出”(平移)其内部视图),但不会被永久禁用(否则它将停止被调用)。通过将其设置回true,它可以在发出故障后立即重新启用此侦听器。

第5步:让多个手势通过相互覆盖来工作

最后但并非最不重要的是,这是一个非常非常重要的步骤,因为它允许平移手势和滚动手势独立工作,而不是只有一个人总是覆盖另一个。

func gestureRecognizer(_: UIGestureRecognizer,
    shouldRecognizeSimultaneouslyWith shouldRecognizeSimultaneouslyWithGestureRecognizer:UIGestureRecognizer) -> Bool {
    return true
}

就是这样!这是关于SO的大量研究(主要是发现什么不起作用)和实验,但它们都汇集在一起​​。

这是为了期望您已经编写了滚动视图中的对象的平移手势识别器,您正在“拉出”以及如何处理其状态(.ended.changed等)。我建议如果您需要帮助,请搜索SO。有很多答案。

如果您对此答案有任何其他疑问,请告诉我们。

以上是关于同一视图上的多个手势(从可滚动视图中拖出视图)无法正常工作?的主要内容,如果未能解决你的问题,请参考以下文章

多个 UIView 上的 UITapGestureRecognizer(滚动视图子视图)

缩放时,UIScrollview 子视图无法识别手势

从 UIScrollview 向前触摸和手势到视图

需要在icarousel中拖放视图

UITableViewCell 上的 UIPanGestureRecognizer 覆盖 UITableView 的滚动视图手势识别器

UIScrollview 子视图在缩放后无法识别平移手势