如何使用"shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer"

How to use "shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer"

我正在尝试以编程方式在屏幕右侧创建 6 个 CGRect 对象,其中三个为蓝色,三个为红色。然后用户将它们拖动到屏幕的左侧,他们需要拖动蓝色块,目标是相同颜色的淡出区域。下图描述了我正在尝试做的事情。

问题是,当我将任何块拖到任何褪色区域上时,它会接受该块并将其删除。我意识到我需要实施 shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer 但我对如何实施感到困惑。我知道 gestureRecognizerotherGestureRecognizer 代表两个冲突的对象,但我不知道如何用我的其余代码实现它,以便我可以确保只有蓝色块可以通过褪色的蓝色块并激活删除功能。下面是我的。

import UIKit

class ViewController: UIViewController {

    private struct Constants {
        static let padding: CGFloat = 75
        static let correctPadding: CGFloat = 0
        static let blockDimension: CGFloat = 50
    }

    var targetView0: UIView?
    var targetView1: UIView?
    var targetView2: UIView?
    var targetView3: UIView?
    var targetView4: UIView?
    var targetView5: UIView?
    
    var beginningPosition: CGPoint = .zero
    var initialMovableViewPosition: CGPoint = .zero

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        addMovableViews(count: 6)
        addtargetView0()
        addtargetView1()
        addtargetView2()
        addtargetView3()
        addtargetView4()
        addtargetView5()
    }
    //This programtically creates the blocks w/ x and y
    private func addMovableViews(count: Int) {
        var xOffset = Constants.padding
        for i in 0..<count {
            print(i)
            let movableView = UIView(frame: CGRect(x: 700, y: xOffset + 120, width: Constants.blockDimension, height: Constants.blockDimension))
            if (i == 0) {
                movableView.backgroundColor = .blue
                view.addSubview(movableView)
                let item0 = UIPanGestureRecognizer(target: self, action: #selector(touched))
                movableView.addGestureRecognizer(item0)
            } else if (i == 1) {
                movableView.backgroundColor = .red
                view.addSubview(movableView)
                let item1 = UIPanGestureRecognizer(target: self, action: #selector(touched))
                movableView.addGestureRecognizer(item1)
                
            } else if (i == 2) {
                movableView.backgroundColor = .blue
                view.addSubview(movableView)
                let item2 = UIPanGestureRecognizer(target: self, action: #selector(touched))
                movableView.addGestureRecognizer(item2)
                
            } else if (i == 3) {
                movableView.backgroundColor = .red
                view.addSubview(movableView)
                let item3 = UIPanGestureRecognizer(target: self, action: #selector(touched))
                movableView.addGestureRecognizer(item3)
                
            } else if (i == 4) {
                movableView.backgroundColor = .blue
                view.addSubview(movableView)
                let item4 = UIPanGestureRecognizer(target: self, action: #selector(touched))
                movableView.addGestureRecognizer(item4)
                
            } else if (i == 5) {
                movableView.backgroundColor = .red
                view.addSubview(movableView)
                let item5 = UIPanGestureRecognizer(target: self, action: #selector(touched))
                movableView.addGestureRecognizer(item5)
                
            } else if (i == 6) {
                movableView.backgroundColor = .blue
                view.addSubview(movableView)
                let item6 = UIPanGestureRecognizer(target: self, action: #selector(touched))
                movableView.addGestureRecognizer(item6)
            }
            xOffset += Constants.blockDimension + Constants.padding
        }
        
    }
    
    private func addtargetView0() {
        let xOffset = Constants.correctPadding
        targetView0 = UIView(frame: CGRect(x: xOffset + 100, y: 500, width: Constants.blockDimension, height: Constants.blockDimension))
        targetView0?.backgroundColor = UIColor(red: 51/255.0, green: 153/255.0, blue: 255/255.0, alpha: 1)
        view.addSubview(targetView0!)
    }
    
    private func addtargetView1() {
        let xOffset = Constants.correctPadding
        targetView1 = UIView(frame: CGRect(x: 100 + xOffset + 50, y: 500, width: Constants.blockDimension, height: Constants.blockDimension))
        targetView1?.backgroundColor = UIColor(red: 255/255.0, green: 102/255.0, blue: 102/255.0, alpha: 1)
        view.addSubview(targetView1!)
    }
    
    private func addtargetView2() {
        let xOffset = Constants.correctPadding
        targetView2 = UIView(frame: CGRect(x: 100 + xOffset + 100, y: 500, width: Constants.blockDimension, height: Constants.blockDimension))
        targetView2?.backgroundColor = UIColor(red: 51/255.0, green: 153/255.0, blue: 255/255.0, alpha: 1)
        view.addSubview(targetView2!)
    }
    
    private func addtargetView3() {
        let xOffset = Constants.correctPadding
        targetView3 = UIView(frame: CGRect(x: 100 + xOffset + 150, y: 500, width: Constants.blockDimension, height: Constants.blockDimension))
        targetView3?.backgroundColor = UIColor(red: 255/255.0, green: 102/255.0, blue: 102/255.0, alpha: 1)
        view.addSubview(targetView3!)
    }
    
    private func addtargetView4() {
        let xOffset = Constants.correctPadding
        targetView4 = UIView(frame: CGRect(x: 100 + xOffset + 200, y: 500, width: Constants.blockDimension, height: Constants.blockDimension))
        targetView4?.backgroundColor = UIColor(red: 51/255.0, green: 153/255.0, blue: 255/255.0, alpha: 1)
        view.addSubview(targetView4!)
    }
    
    
    private func addtargetView5() {
        let xOffset = Constants.correctPadding
        targetView5 = UIView(frame: CGRect(x: 100 + xOffset + 250, y: 500, width: Constants.blockDimension, height: Constants.blockDimension))
        targetView5?.backgroundColor = UIColor(red: 255/255.0, green: 102/255.0, blue: 102/255.0, alpha: 1)
        view.addSubview(targetView5!)
    }

    //Gesture recognizer
    @objc private func touched(_ gestureRecognizer: UIGestureRecognizer) {
        if let touchedView = gestureRecognizer.view {
            if gestureRecognizer.state == .began {
                beginningPosition = gestureRecognizer.location(in: touchedView)
                initialMovableViewPosition = touchedView.frame.origin
            } else if gestureRecognizer.state == .ended {
                //Moves it back to where it was
//                touchedView.frame.origin = initialMovableViewPosition
                
            } else if gestureRecognizer.state == .changed {
                let locationInView = gestureRecognizer.location(in: touchedView)
                touchedView.frame.origin = CGPoint(x: touchedView.frame.origin.x + locationInView.x - beginningPosition.x, y: touchedView.frame.origin.y + locationInView.y - beginningPosition.y)
                if touchedView.frame.intersects(targetView0!.frame) {
                    touchedView.removeFromSuperview() //Might uncomment when everything is set up so it deals with the overlay issue
//                    gestureRecognizer.isEnabled = false
                    targetView0?.backgroundColor = .blue
                    initialMovableViewPosition = .zero
                }
                if touchedView.frame.intersects(targetView1!.frame) {
                    touchedView.removeFromSuperview() //Might uncomment when everything is set up so it deals with the overlay issue
//                    gestureRecognizer.isEnabled = false
                    targetView1?.backgroundColor = .red
                    initialMovableViewPosition = .zero
                }
                if touchedView.frame.intersects(targetView2!.frame) {
                    touchedView.removeFromSuperview() //Might uncomment when everything is set up so it deals with the overlay issue
//                    gestureRecognizer.isEnabled = false
                    targetView2?.backgroundColor = .blue
                    initialMovableViewPosition = .zero
                }
                if touchedView.frame.intersects(targetView3!.frame) {
                    touchedView.removeFromSuperview() //Might uncomment when everything is set up so it deals with the overlay issue
//                    gestureRecognizer.isEnabled = false
                    targetView3?.backgroundColor = .red
                    initialMovableViewPosition = .zero
                }
                if touchedView.frame.intersects(targetView4!.frame) {
                    touchedView.removeFromSuperview() //Might uncomment when everything is set up so it deals with the overlay issue
//                    gestureRecognizer.isEnabled = false
                    targetView4?.backgroundColor = .blue
                    initialMovableViewPosition = .zero
                }
                if touchedView.frame.intersects(targetView5!.frame) {
                    touchedView.removeFromSuperview() //Might uncomment when everything is set up so it deals with the overlay issue
//                    gestureRecognizer.isEnabled = false
                    targetView5?.backgroundColor = .red
                    initialMovableViewPosition = .zero
                }
            }
        }
    }
    
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
           shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer)
            -> Bool {
                // If the gesture recognizers are on diferent views, do not allow
                // simultaneous recognition.
                if gestureRecognizer.view != otherGestureRecognizer.view {
                   return false
                }
     
                return true
    }
    
    init() {
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

有人可以解释如何合并 shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer、link 一个完整的项目来做类似的事情,或者引导我完成它。感谢任何帮助。

我明白了。它基本上只是在手势 if 语句中。

@objc private func touched(_ gestureRecognizer: UIGestureRecognizer) {
        if let touchedView = gestureRecognizer.view {
            if gestureRecognizer.state == .began {
                beginningPosition = gestureRecognizer.location(in: touchedView)
                initialMovableViewPosition = touchedView.frame.origin
            } else if gestureRecognizer.state == .ended {
                //Moves it back to where it was
//                touchedView.frame.origin = initialMovableViewPosition
                
            } else if gestureRecognizer.state == .changed {
                let locationInView = gestureRecognizer.location(in: touchedView)
                touchedView.frame.origin = CGPoint(x: touchedView.frame.origin.x + locationInView.x - beginningPosition.x, y: touchedView.frame.origin.y + locationInView.y - beginningPosition.y)
                if ((touchedView.backgroundColor == .blue) && (targetView0?.backgroundColor == UIColor(red: 51/255.0, green: 153/255.0, blue: 255/255.0, alpha: 1)) && (touchedView.frame.intersects(targetView0!.frame))) {
                        touchedView.removeFromSuperview() //Might uncomment when everything is set up so it deals with the overlay issue
    //                    gestureRecognizer.isEnabled = false
                        targetView0?.backgroundColor = .blue
                        initialMovableViewPosition = .zero
                }
                if ((touchedView.backgroundColor == .red) && (targetView1?.backgroundColor == UIColor(red: 255/255.0, green: 102/255.0, blue: 102/255.0, alpha: 1)) && (touchedView.frame.intersects(targetView1!.frame))) {
                        touchedView.removeFromSuperview() //Might uncomment when everything is set up so it deals with the overlay issue
    //                    gestureRecognizer.isEnabled = false
                        targetView1?.backgroundColor = .red
                        initialMovableViewPosition = .zero
                }
                if ((touchedView.backgroundColor == .blue) && (targetView2?.backgroundColor == UIColor(red: 51/255.0, green: 153/255.0, blue: 255/255.0, alpha: 1)) && (touchedView.frame.intersects(targetView2!.frame))) {
                        touchedView.removeFromSuperview() //Might uncomment when everything is set up so it deals with the overlay issue
    //                    gestureRecognizer.isEnabled = false
                        targetView2?.backgroundColor = .blue
                        initialMovableViewPosition = .zero
                }
                if ((touchedView.backgroundColor == .red) && (targetView3?.backgroundColor == UIColor(red: 255/255.0, green: 102/255.0, blue: 102/255.0, alpha: 1)) && (touchedView.frame.intersects(targetView3!.frame))) {
                        touchedView.removeFromSuperview() //Might uncomment when everything is set up so it deals with the overlay issue
    //                    gestureRecognizer.isEnabled = false
                        targetView3?.backgroundColor = .red
                        initialMovableViewPosition = .zero
                }
                if ((touchedView.backgroundColor == .blue) && (targetView4?.backgroundColor == UIColor(red: 51/255.0, green: 153/255.0, blue: 255/255.0, alpha: 1)) && (touchedView.frame.intersects(targetView4!.frame))) {
                        touchedView.removeFromSuperview() //Might uncomment when everything is set up so it deals with the overlay issue
    //                    gestureRecognizer.isEnabled = false
                        targetView4?.backgroundColor = .blue
                        initialMovableViewPosition = .zero
                }
                if ((touchedView.backgroundColor == .red) && (targetView5?.backgroundColor == UIColor(red: 255/255.0, green: 102/255.0, blue: 102/255.0, alpha: 1)) && (touchedView.frame.intersects(targetView5!.frame))) {
                        touchedView.removeFromSuperview() //Might uncomment when everything is set up so it deals with the overlay issue
    //                    gestureRecognizer.isEnabled = false
                        targetView5?.backgroundColor = .red
                        initialMovableViewPosition = .zero
                }
            }
        }
    }

你检查gestureRecognizer的背景颜色是不是蓝色,通过UIColor检查targetView的背景颜色是不是淡蓝色,视图是否相交,然后你做你需要的。您甚至可以使用额外的 if 语句按照您想要的顺序填充目标视图。

这里有一些问题。首先,您的手势的代表没有被设置。为此,您需要 ViewController 来扩展 UIGestureRecognizerDelegate,就像这样 class ViewController: UIViewController, UIGestureRecognizerDelegate。然后,您需要将每个手势的委托设置为 ViewController:

let item0 = UIPanGestureRecognizer(target: self, action: #selector(touched))
item0.delegate = self
movableView.addGestureRecognizer(item0)

其次,即使设置了代表,也永远不会调用 shouldRecognizeSimultaneouslyWith,因为每个可移动视图只有一种类型的手势 (UIPanGestureRecognizer)。您的目标视图本身没有分配给它们的任何手势识别器。如果你想测试 shouldRecognizeSimultaneouslyWith 何时被调用,你可以尝试执行以下操作:

let item0 = UIPanGestureRecognizer(target: self, action: #selector(touched))
item0.delegate = self
movableView.addGestureRecognizer(item0)
                
// Add another gesture
let testitem = UITapGestureRecognizer(target: self, action: #selector(test))
movableView.addGestureRecognizer(testitem)

在上面的示例中,UITapGestureRecognizerUIPanGestureRecognizer 都会在您开始拖动可移动视图时触发。

===解决方案===

我建议不要使用 shouldRecognizeSimultaneouslyWith,而是向每个可移动视图和目标视图添加标签吗?例如,对于所有蓝色可移动和目标视图,movableView.tag = 0,对于所有红色可移动和目标视图,movableView.tag = 1.

然后,在您的 touched 操作中,您可以像这样检查标签是否匹配:

if touchedView.frame.intersects(targetView0!.frame) {
   if touchedView.tag == targetView0?.tag {
      touchedView.removeFromSuperview() 
      targetView0?.backgroundColor = .blue
      initialMovableViewPosition = .zero
   }
}