Chris
Chris

Reputation: 2274

Handle two Gesture Recognizer simultaneously

The problem is quite simple: I have a ViewController on which I have a GestureRecognizer:

    panGR = UIPanGestureRecognizer(target: self,
              action: #selector(handlePan(gestureRecognizer:)))
    view.addGestureRecognizer(panGR)

In this ViewController I also have a class WhishlistTableViewController: UITableViewController on which I have the "swipe-to-delete"-function:

override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
    let deleteAction = UIContextualAction(style: .destructive, title: "Löschen") {[weak self] _, _, completionHandler in
        self!.deleteWishDelegate?.deleteWish(indexPath)
    completionHandler(true)
    }
    deleteAction.backgroundColor = .red
    deleteAction.image = UIImage(systemName: "trash")
    let configuration = UISwipeActionsConfiguration(actions: [deleteAction])
    configuration.performsFirstActionWithFullSwipe = false
    return configuration
}

Here is a Video of my panGR in action: Screenvideo

This is my handlePan function:

// handle swqipe down gesture
@objc private func handlePan(gestureRecognizer:UIPanGestureRecognizer) {
    // calculate the progress based on how far the user moved
    let translation = panGR.translation(in: nil)
    let progress = translation.y / 2 / view.bounds.height

  switch panGR.state {
  case .began:
    // begin the transition as normal
    self.dismissView()
    break
  case .changed:

    Hero.shared.update(progress)

  default:
    // finish or cancel the transition based on the progress and user's touch velocity
       if progress + panGR.velocity(in: nil).y / view.bounds.height > 0.3 {
        self.dismissView()
         Hero.shared.finish()
       } else {
         Hero.shared.cancel()
       }
  }
}

The problem is that these two collide. The "swipe-to-delete" only works if I disable the other GestureRecognizer. Why is that and how can I solve this ?

Upvotes: 0

Views: 1159

Answers (4)

Chris
Chris

Reputation: 2274

With the help of Mitesh Mistri I got it working. One more thing that was missing was a function to disable the panGR inside the tableView because otherwise the user wouldnt be able to scroll. These are the two function that made it work:

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

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    if self.theTableView.view.bounds.contains(touch.location(in: self.theTableView.view)) {
        return false
    }
    return true
}

Upvotes: 0

Soren Nguia
Soren Nguia

Reputation: 24

Normally this is simple,

Firstly , you don't need to add a gesture recognizer to the view,

we already have the function "touchesBegan" which Tells this object that one or more new touches occurred in a view or window.

so in your code you just need to use it instead of the gesture recognizer and it will works.

class ViewController: UIViewController,UITableViewDataSource {
  
    
    let tableView:UITableView = {
     let table =  UITableView()
        table.backgroundColor = .blue
        table.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        return table
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
    //    tableView.delegate = self
        tableView.dataSource = self
        view.addSubview(tableView)
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        tableView.frame = CGRect(x: 0, y: view.frame.height/2, width: view.frame.width, height:view.frame.height/2 )
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
          return 20
      }
      
      func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
          let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
          cell.backgroundColor = .blue
          return cell
      }
      

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else {
            return
        }// we store the touch
        
        let location = touch.location(in: view)//we retrieve the location of the touch
        
        if view.bounds.contains(location){//we check if our touch is inside our view
            
            // if our touch is inside  the view ,do the action you wanted to do with your gesture recognizer
            print("inside the view, outside tableView(Controller)")
        }else{
            // else here means here that  this is not inside the view, so it is inside  the tableView(Controller)
            // if this is not inside the view, you can select tableView,rows,swipe-to-delete etc, basically do whatever action you want with it
        }
    }
   
    
  
}



Upvotes: -1

Nandish
Nandish

Reputation: 1162

When you do pan gesture on top of the view the gesture pan recognizer will be invoked. If the tableview is part of the view the gesture handlePan will be called when you try to do swipe-to-delete function.

If you want 2 separate behavior then separate the part of the view in which you want gesture handlePan to execute and tableview where you want swipe-to-delete function to execute.

Something like below :

   panGR = UIPanGestureRecognizer(target: self,
          action: #selector(handlePan(gestureRecognizer:)))
   gestureView.addGestureRecognizer(panGR)
   view.addSubView(gestureView)

EDIT: You can also get the location/co-ordinates of the location pressed and you can calculate which areas to ignore.

    currentLocation = gesture.location(in: self.view)
      

Upvotes: -1

Mitesh Mistri
Mitesh Mistri

Reputation: 459

You should try this.

class YourViewController: UIViewController, UIGestureRecognizerDelegate

in viewDidLoad

yourGesture.delegate = self

and finally

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

    // If you wish to do conditionally.
    /*if (gestureRecognizer is UIPanGestureRecognizer || gestureRecognizer is UIRotationGestureRecognizer) {
        return true
    } else {
        return false
    }*/
}

Upvotes: 3

Related Questions