如何同时处理 UIScrollView 和 UIScrollView 之上的 UIView 的触摸

How to handle touches by an UIView on top of a UIScrollView and the UIScrollView both at the same time

我有以下层次结构,控制器的根视图。添加了两个子视图,一个标准的 UIView 和一个 UIScrollView。 UIView 在上面。

我想要做的是在那个 UIView 上接收触摸,意思是 touchesBegan、touchesMoved... 像这样:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    print("Began")
    super.touchesBegan(touches, with: event)
    next?.touchesBegan(touches, with: event)
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    print("Moved")
    super.touchesMoved(touches, with: event)
    next?.touchesMoved(touches, with: event)
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    print("Ended")
    super.touchesEnded(touches, with: event)
    next?.touchesEnded(touches, with: event)
}

但同时我希望卷轴起作用。下面的 UIScrollView,我希望它像往常一样继续滚动。

这似乎是不可能的。如果我的视图处理触摸,那么我不能将它们转发到 UIScrollView。但我想要两件事:在顶部视图上查看原始触摸,同时滚动视图照常工作。

我怎样才能做到这一点?

反之亦然。意思是,顶部的滚动视图像往常一样滚动,但我还在下面的视图上收到原始触摸(touchesBegan,touchesMoved ...)。

遵循这个 answer 对我有用,尽管有一些细微的变化。请注意,对于此解决方案,UIScrollView 位于 UIView 之上。首先,您需要创建一个 UIScrollView 的子类并覆盖触摸方法。

protocol CustomScrollViewDelegate {
    func scrollViewTouchBegan(_ touches: Set<UITouch>, with event: UIEvent?)
    func scrollViewTouchMoved(_ touches: Set<UITouch>, with event: UIEvent?)
    func scrollViewTouchEnded(_ touches: Set<UITouch>, with event: UIEvent?)
}

class CustomScrollView: UIScrollView {
    
    var customDelegate: CustomScrollViewDelegate?
    
    override func awakeFromNib() {
        super.awakeFromNib()
        
        for gesture in self.gestureRecognizers ?? [] {
            gesture.cancelsTouchesInView = false
            gesture.delaysTouchesBegan = false
            gesture.delaysTouchesEnded = false
        }
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        customDelegate?.scrollViewTouchBegan(touches, with: event)
        super.touchesBegan(touches, with: event)
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        customDelegate?.scrollViewTouchMoved(touches, with: event)
        super.touchesMoved(touches, with: event)
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        customDelegate?.scrollViewTouchEnded(touches, with: event)
        super.touchesEnded(touches, with: event)
    }
}

记下 awakeFromNib() 中的代码。 UIScrollView 有自己的一套手势。因此,对于每个手势,delaysTouchesBegandelaysTouchesEnded 需要 false 以防止触摸事件延迟。

最后,只需将委托分配给您的 ViewController 并像这样实现方法。

class ViewController: UIViewController {
    
    @IBOutlet weak var scrollView: CustomScrollView!
    @IBOutlet weak var touchView: UIView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        scrollView.customDelegate = self
    }
}

extension ViewController: CustomScrollViewDelegate {
    func scrollViewTouchBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        // Check if touch location is within the bounds of the UIView
        if let touch = touches.first {
            let position = touch.location(in: view)
            if touchView.bounds.contains(position) {
                print("Began")
            }
        }
    }
    
    func scrollViewTouchMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        // Check if touch location is within the bounds of the UIView
        if let touch = touches.first {
            let position = touch.location(in: view)
            if touchView.bounds.contains(position) {
                print("Moved")
            }
        }
    }
    
    func scrollViewTouchEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        // Check if touch location is within the bounds of the UIView
        if let touch = touches.first {
            let position = touch.location(in: view)
            if touchView.bounds.contains(position) {
                print("Ended")
            }
        }
    }
}