如何处理UICollectionviewCell或UITableViewCell内的按钮触摸

时间:2017-07-12 15:58:24

标签: ios swift uitableview uicollectionview uicollectionviewcell

这是一个问题,当我创建细胞时,我总是处于两难境地。

假设我有一个自定义单元格(PlaceCell),我在不同的控制器和集合视图中使用它。它有一个标签,用于标识地名(PlaceNameLabel),并会在用户点按它时导航到详细位置。

这与控制器,集合视图或使用单元格的位置无关,这与使用位置无关。

所以我必须将PlaceNameLabel's UITapGestureRecognizer代码放在PlaceCell类中。

class PlaceCell: UICollectionViewCell {
     @IBOutlet weak var placeName: UILabel!
     override func awakeFromNib() {
        super.awakeFromNib()
        initEventListeners()
    }

    func initEventListeners() {
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, 
                   action: #selector(handlePlaceNameTouch))
    }

    func handlePlaceNameTouch() {
        // I have to push a new view controller here
    }
}

但是如果我想推送位置细节视图控制器,我需要访问控制器。如果我想访问控制器,我有两个选择,这就是我陷入困境的地方,我已经阅读了大量的SO问题答案,其中大多数建议第二个选项,但我认为第一个是更好的方法。但我不知道引用单元内的控制器是否有问题。

您能否分享您的意见或任何其他方法来处理此问题?

第一选择

引用单元格内的控制器,

extension MyController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PlaceCell", for: indexPath) as! PlaceCell
        cell.controller = self
        return cell
    }
}

class PlaceCell: UICollectionViewCell {
     var controller: UIViewController?
     @IBOutlet weak var placeName: UILabel!
     override func awakeFromNib() {
        super.awakeFromNib()
        initEventListeners()
    }

    func initEventListeners() {
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, 
                   action: #selector(handlePlaceNameTouch))
    }

    func handlePlaceNameTouch() {
        controller.navigationController.pushViewController(PlaceDetailController(), 
                                             animated: true)
    }
}

第二个选项

创建协议和委托,将事件传递给控制器​​并执行您想要的任何操作, (我认为这不太好,因为动作是关于单元格的,我必须多次创建协议功能,因为我在不同的控制器中使用这个单元格)

protocol PlaceCellDelegate {
  func handlePlaceNameTouch()
}

class PlaceCell: UICollectionViewCell {
    var delegate: PlaceCellDelegate?
    @IBOutlet weak var placeName: UILabel!
    override func awakeFromNib() {
        super.awakeFromNib()
        initEventListeners()
    }

    func initEventListeners() {
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, 
                           action: #selector(handlePlaceNameTouch))
    }

    func handlePlaceNameTouch() {
        delegate?.handlePlaceNameTouch()
    }
}

extension MyController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, PlaceCellDelegate {
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PlaceCell", for: indexPath) as! PlaceCell
        cell.delegate = self
        return cell
    }

    func handlePlaceNameTouch() {
          self.navigationController.pushViewController(PlaceDetailController(), animated: true)
    }
}

2 个答案:

答案 0 :(得分:0)

虽然您可以选择两种方式,但如果您犹豫不决,我会推荐代理方法,因为它更加“快速”#34;这样做的方式,使细胞可重复使用。

这是使用onTapHandler的第三个选项,它不使用委托模式并仍然保持单元格可重用:

class PlaceCell: UICollectionViewCell {

     // A handler called when the button is pressed
     var onTapHandler: (() -> Void)?

     @IBOutlet weak var placeName: UILabel!

     override func awakeFromNib() {
        super.awakeFromNib()
        initEventListeners()
    }

    func initEventListeners() {
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, 
                   action: #selector(handlePlaceNameTouch))
    }

    func handlePlaceNameTouch() {

        // Make sure the handler exists, else...
        guard let handler = self.onTapHandler else {
            return
        }

        // Execute handler
        handler()
    }
}

然后你会这样使用它:

extension MyController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, PlaceCellDelegate {

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PlaceCell", for: indexPath) as! PlaceCell

        cell.onTapHandler = { [weak self] in
            self?.navigationController.pushViewController(PlaceDetailController(), animated: true)
        }

        return cell
    }

}

答案 1 :(得分:0)

您无需专门处理细胞选择。无需使用UITapGestureRecognizer或无需实施自己的协议来检测单元格选择。

UICollectionViewUITableView有自己的协议。

UICollectionView - UICollectionViewDelegate 协议

的小组选择代理
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)

UITableView - UITableViewDelegate 协议

的小组选择代理
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)

确认protocol&在delegate

上设置UIViewController
class viewcontroller : UICollectionViewDelegate {
   @IBOutlet weak collectionView: UICollectionView!

   func viewDidLoad() {
       super.viewDidLoad()
       collectionView.delegate = self
   }
}

在视图控制器中实现协议方法,我在上面提到过。

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
   //indexPath - Index path of selected cell.

   // Add you cell selection logic here. 
}

点击单元格时会触发此方法。在此方法内部从数据源数组中获取模型。并根据用户选择的单元格(模型)导航到详细视图。