UICollectionViewCell中UIButton的UIAction点击多次触发问题
问题原因
UICollectionView的cell采用复用机制,每次调用cellForItemAt时,你都会给按钮添加一个新的UIAction实例。不同于addTarget会自动去重重复的target+selector组合,UIAction每次添加都是独立的对象,不会自动去重。当cell被复用后,之前添加的UIAction并没有被移除,导致按钮上绑定了多个UIAction,点击时所有绑定的动作都会触发。
解决方法
方法1:利用cell复用钩子清理旧Action
在PhotoCell中重写prepareForReuse方法,在cell被复用前移除按钮上对应事件的所有Action:
class PhotoCell: UICollectionViewCell { @IBOutlet weak var deleteButton: UIButton! override func prepareForReuse() { super.prepareForReuse() // 移除touchUpInside事件下的所有Action deleteButton.removeAllActions(for: .touchUpInside) } }
这样每次cell复用前都会清空旧的Action,再添加新的就不会出现累积触发的问题。
方法2:仅给按钮绑定一次Action,通过闭包传递事件
把Action的绑定逻辑放在cell内部(比如awakeFromNib),只绑定一次,然后用闭包将点击事件传递给ViewController。这种方式更符合MVC的职责划分,也从根源避免了重复绑定:
首先修改PhotoCell:
class PhotoCell: UICollectionViewCell { @IBOutlet weak var deleteButton: UIButton! // 定义闭包用于传递点击事件 var onDeleteTap: (() -> Void)? override func awakeFromNib() { super.awakeFromNib() // 只绑定一次Action let action = UIAction { [weak self] _ in self?.onDeleteTap?() } deleteButton.addAction(action, for: .touchUpInside) } }
然后修改ViewController的cellForItemAt:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PhotoCell", for: indexPath) as? PhotoCell else { return UICollectionViewCell() } // 给闭包赋值,每次复用更新对应的逻辑 cell.onDeleteTap = { print("Delete button tapped!!!", indexPath.row) } return cell }
补充说明
addTarget没有这个问题的核心原因是:当调用addTarget(_:action:for:)时,如果同一个target和selector已经存在于该事件的动作列表中,系统不会重复添加;而UIAction是基于实例的,每次创建的UIAction都是新对象,系统会视为不同的动作,所以会不断累积。
内容的提问来源于stack exchange,提问作者Yogesh Patel
相关产品推荐
相关产品推荐

