skip to Main Content

I added UITapGestureRecognizer for single and double clicks on UITableViewCell, and let the single click gesture be responded to when the double click gesture fails, but I don’t want the tableView’s didSelected to be responded to. Is there any way to handle this in the TableViewCell class?

class ViewController: UIViewController {
    
    let dataSource = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
    
    lazy var tableView: UITableView = {
        let view = UITableView(frame: CGRectZero)
        view.dataSource = self
        view.delegate = self
        view.register(TableViewCell.self, forCellReuseIdentifier: TableViewCell.description())
        return view
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        self.view.addSubview(tableView);
        tableView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
    }
}

extension ViewController: UITableViewDataSource, UITableViewDelegate {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        dataSource.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if let cell = tableView.dequeueReusableCell(withIdentifier: TableViewCell.description(), for: indexPath) as? TableViewCell {
            return cell;
        }
        return UITableViewCell()
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        40
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
         
    }
}

class TableViewCell: UITableViewCell {
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        let singleTapGesture = UITapGestureRecognizer(target: self, action: #selector(tap))
        singleTapGesture.cancelsTouchesInView = false;
        addGestureRecognizer(singleTapGesture)
        
        let doubleTapGesture = UITapGestureRecognizer(target: self, action: #selector(doubleTap))
        doubleTapGesture.numberOfTapsRequired = 2
        addGestureRecognizer(doubleTapGesture)
       
        singleTapGesture.require(toFail: doubleTapGesture)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    @objc func tap() {
        
    }
    
    @objc func doubleTap() {
        
    }
}

I want only tap and doubleTap to be called in case of single and double click, but didSelectRowAt is not called

2

Answers


  1. Because you’re conforming to UITableViewDelegate, and then by default, it will trigger didSelectRowAt function. You can remove this delegate, and let the table calculate the cell’s height with UITableView.automaticDimension.

    view.dataSource = self
    //view.delegate = self <- here
    

    I also recommend using contentView instead of directly a table cell. Because this is the actual content that cell displays, excluding editing mode/cell transitions, etc.

    contentView.addGestureRecognizer
    

    Update: If that the case you still need to conform delegate. You can add a kind of tapableView above the entire contentView.

    let tapableView = UIView() //<- this view should be above all the content's sub views
    tapableView.backgroundColor = .clear
    contentView.addSubview(tapableView)
    ...
    
    tapableView.addGestureRecognizer(singleTapGesture)
    tapableView.addGestureRecognizer(doubleTapGesture)
    ...
    
    @objc func tap() {
        print("Single tap")
    }
        
    @objc func doubleTap() {
        print("Double tap")
    }
    
    
    Single tap
    Double tap
    
    Login or Signup to reply.
  2. It’s not entirely clear what you mean when you say you don’t want "didSelected to be responded to"

    If you don’t have a didSelectRowAt function, the default is to do nothing, so it’s "not responding" to it.

    If you don’t want the cell to highlight, in cellForRowAt you can set:

    if let cell = tableView.dequeueReusableCell(withIdentifier: TableViewCell.description(), for: indexPath) as? TableViewCell {
        cell.selectionStyle = .none
        return cell;
    }
    

    The table view will still internally track that a row is selected, but there will be no visual indication, and if your code does not implement didSelectRowAt there won’t be a "response."

    If you want to prevent the table view from tracking the selected row, you can implement shouldHighlightRowAt:

    func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
        return false
    }
    

    Now, you don’t need cell.selectionStyle = .none and didSelectRowAt will never be called.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search