如何给 UITableViewCell 添加手势?

How to add gesture to UITableViewCell?

我想向 UITableView 中的每个单元格添加一个点击手势来编辑其中的内容。添加手势的两种方法是在代码中或通过故事板。我都试过了,但都失败了。

我可以通过故事板拖放向 table 中的 每个 单元格添加手势吗?它似乎只向第一个单元格添加手势。在代码中添加手势,我写了类似

addGestureRecognizer(UITapGestureRecognizer(target: self,action:#selector(MyTableViewCell.tapEdit(_:))))

addGestureRecognizer(UITapGestureRecognizer(target: self, action:"tapEdit:"))

两者都有效。但我想让 UITableViewController 处理这个手势,因为它对数据源做了一些事情。如何写我的目标和行动?

编辑:

addGestureRecognizer(UITapGestureRecognizer(target: MasterTableViewController.self, action:#selector(MasterTableViewController.newTapEdit(_:)))

它导致错误说,无法识别的选择器发送到 class 0x106e674e0...

为UITableViewCell添加手势,您可以按照以下步骤操作:

首先,将手势识别器添加到 UITableView

tapGesture = UITapGestureRecognizer(target: self, action: #selector(tableViewController.tapEdit(_:)))
tableView.addGestureRecognizer(tapGesture!)
tapGesture!.delegate = self

然后,定义选择器。使用 recognizer.locationInView 定位您在 tableView 中点击的单元格。您可以通过 tapIndexPath 访问数据源中的数据,这是用户点击的单元格的索引路径。

func tapEdit(recognizer: UITapGestureRecognizer)  {
    if recognizer.state == UIGestureRecognizerState.Ended {
        let tapLocation = recognizer.locationInView(self.tableView)
        if let tapIndexPath = self.tableView.indexPathForRowAtPoint(tapLocation) {
            if let tappedCell = self.tableView.cellForRowAtIndexPath(tapIndexPath) as? MyTableViewCell {
                //do what you want to cell here

            }
        }
    }
}

可以直接在TableView单元格中添加手势并访问viewController中的数据源,需要设置一个delegate:

在您的自定义单元格中:

import UIKit


class MyTableViewCell: UITableViewCell {

    var delegate: myTableDelegate?

    override func awakeFromNib() {
        super.awakeFromNib()

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(MyTableViewCell.tapEdit(_:)))
        addGestureRecognizer(tapGesture)
        //tapGesture.delegate = ViewController()

    }

    func tapEdit(sender: UITapGestureRecognizer) {
        delegate?.myTableDelegate()
    }

}

protocol myTableDelegate {
    func myTableDelegate() 
}

在你的viewController中:

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UIGestureRecognizerDelegate, myTableDelegate {

    @IBOutlet var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.delegate = self
        tableView.dataSource = self
        // Do any additional setup after loading the view, typically from a nib.
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 35
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as? MyTableViewCell

        cell?.delegate = self

        return cell!
    }

    func myTableDelegate() {
        print("tapped")
        //modify your datasource here
    }

}

但是,此方法可能会导致问题,请参阅 UIGestureRecognizer and UITableViewCell issue。在这种情况下,当滑动手势成功时,选择器由于某种原因被调用两次。我不能说第二种方法不好,因为我还没有找到任何直接证据,但在搜索 Google 之后,似乎第一种方法是标准方法。

您不需要添加手势识别器来实现您正在做的事情。

  • 使用 UITableViewDelegate 方法 tableView:didSelectRowAtIndexPath: 检测哪一行被点击(这正是您的 tapGesture 要做的)然后做你想要的处理。
  • 如果您不喜欢 select 单元格时的灰色指示,请在返回单元格之前在 tableView:didEndDisplayingCell:forRowAtIndexPath: 中键入:
    cell?.selectionStyle = .None

在 awakeFromNib 方法中添加手势似乎更容易并且工作正常。

class TestCell: UITableViewCell {

    override func awakeFromNib() {
        super.awakeFromNib()

        let panGesture = UIPanGestureRecognizer(target: self,
                                            action: #selector(gestureAction))
        addGestureRecognizer(panGesture)
    }

    @objc func gestureAction() {
        print("gesture action")
    }
}

最简单的方法是在自定义 UITableViewCell 中添加手势。设置自定义委托模式的更简单替代方法是通知视图控制器编辑将使用视图控制器可以提供的闭包形式的处理程序,并在用户编辑完成时调用。我假设 textField 用于允许单元格编辑。

class CustomTableViewCell: UITableViewCell {

    func activateTitleEditing() {
        textField.isEnabled = true
        textField.becomeFirstResponder()
    }

    // This will hold the handler closure which the view controller provides
    var resignationHandler: (() -> Void)?

    @objc private func tap(_ recognizer: UITapGestureRecognizer) {
        guard recognizer.state == .ended else { return }
        activateTitleEditing()
    }

    @IBOutlet weak var textField: UITextField! { didSet {
        textField.delegate = self
        let tap = UITapGestureRecognizer(target: self, action: #selector(tap(_:)))
        addGestureRecognizer(tap)
        textField.isEnabled = false
        }}
}

extension CustomTableViewCell: UITextFieldDelegate {
    func textFieldDidEndEditing(_ textField: UITextField) {
        resignationHandler?()
    }
}

在您的自定义 UITableViewController 中,传入处理程序以便能够对您的模型进行更改。不要忘记考虑闭包中可能的内存周期。

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    // initialize and return table view cell
    let cell = tableView.dequeueReusableCell(withIdentifier: K.documentCellIdentifier, for: indexPath)
    assert(cell is CustomTableViewCell, "Document cell dequeuing error")
    let customCell = cell as! DocumentTableViewCell
    customCell.textField.text = documentModel.documents[indexPath.row]
    customCell.resignationHandler = { [weak self, unowned customCell] in
        guard let self = self else { return }
        if let newTitle = customCell.textField.text {
            self.cellModel.cells[indexPath.row] = newTitle
        }
    }
    return customCell
}