Search code examples
iosswiftuiviewuigesturerecognizeruipangesturerecognizer

How to detect and apply pan gesture only to top subview


I have an unknown number of subviews that could appear on a main view called mainCanvasView. My goal is, when the user makes a pan gesture on a subview

  1. detect which subview it is, and bring that subview to top of mainCanvasView
  2. apply a pan animation transformation only to that subview

Right now, I have done the following:

//Added a pan gesture hanlder to mainCanvasView
let panGesture = UIPanGestureRecognizer(target: self, action:#selector(self.handlePanGesture(gesture:)))
self.mainCanvasView.addGestureRecognizer(panGesture)

//Define the method for handling gestures
func handlePanGesture(gesture: UIPanGestureRecognizer) {
    if gesture.state == UIGestureRecognizerState.began || gesture.state == UIGestureRecognizerState.changed {
        var appliedView = UIView(frame: view.frame)
        viewLooper: for view in self.mainCanvasView.subviews {
            let point = gesture.location(in: view)
            if (view.hitTest(point, with: nil) != nil) { //Find the view that the finger interacted with
                appliedView = view
                break viewLooper
            }
        }

        self.mainCanvasView.bringSubview(toFront: appliedView)
        let translation = gesture.translation(in: view)
        appliedView.transform = (appliedView.transform).translatedBy(x: translation.x, y: translation.y)
        gesture.setTranslation(CGPoint(x: 0, y: 0), in: appliedView)
    }
}

As the gif shows, while moving individual subviews is fine, when the subviews slightly overlap each other, it creates a strange effect in which both subviews are moved.

Any recommendations on how I could solve this issue is much appreciated.

enter image description here


Solution

  • Remove Pan gesture Here is better solution (YOU CAN ADD YOUR OWN IMPROVEMENTS HERE )

    I have used touch methods

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        for view in self.viewMain.subviews {
    
            if  let point = touches.first?.location(in: self.view) {
    
                if let viewTouched =  self.view.hitTest(point, with: event),viewTouched == view {
                    viewNew = view
                    viewNew.center = point
                    self.viewMain.bringSubview(toFront: view)
                    print("Tapped")
                    break
                } else {
                    print("Not Tapped")
                }
    
            }
        }
    
    }
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    
        if  let point =  touches.first?.location(in: self.viewMain) {
            viewNew.center = point
        }
    
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        viewNew = UIView()
    }
    

    viewNew is Global UIView Object

    Hope it is helpful to you