UIAction在CollectionViewCell中的UIButton上被多次点击时被调用。

huangapple go评论46阅读模式
英文:

UIAction calling multiple times when tapped on UIButton in CollectionViewCell

问题

当我点击按钮时,它会触发多次。

CollectionViewCell 文件代码

class PhotoCell: UICollectionViewCell {
    @IBOutlet weak var deleteButton: UIButton!
}

ViewController - cellForItemAt 方法实现

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PhotoCell", for: indexPath) as? PhotoCell else { return UICollectionViewCell() }

        let action = UIAction { _ in
            print("Delete button tapped!!!", indexPath.row)
        }

        cell.deleteButton.addAction(action, for: .touchUpInside)

        return cell
    }

如果我配置 UIButton 的 addTarget,那么它可以正常工作,但我不确定为什么使用 addAction 就不起作用。

英文:

When I tap on button it will trigger multiple times.

CollectionViewCell file code

class PhotoCell: UICollectionViewCell {
    @IBOutlet weak var deleteButton: UIButton!
}

ViewController - cellforItemAt method implementation

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PhotoCell", for: indexPath) as? PhotoCell else { return UICollectionViewCell() }

        let action = UIAction { _ in
            print("Delete button tapped!!!", indexPath.row)
        }

        cell.deleteButton.addAction(action, for: .touchUpInside)

        return cell
    }

If I configure UIButton addTarget then it work fine but I am not sure why it's not working with addAction.

答案1

得分: 1

因为UICollectionView重用了一些PhotoCell的实例,所以动作被触发了多次。这意味着通过dequeueReusableCell()返回的PhotoCelldeleteButton可能已经添加了动作,并且最终会为相同的事件具有多个动作。

一个可能的解决方案是在PhotoCell中覆盖prepareForReuse()方法,在那里移除动作。

class PhotoCell: UICollectionViewCell {
    @IBOutlet weak var deleteButton: UIButton!
    var actions: [(UIAction, UIControl.Event)] = []

    func addAction(_ action: UIAction, for event: UIControl.Event) {
        self.actions.append((action, event))
        self.deleteButton.addAction(action, for: event)
    }

    override func prepareForReuse() {
        super.prepareForReuse()
        for tuple in self.actions {
            self.deleteButton.removeAction(tuple.0, for: tuple.1)
        }
        self.actions = []
    }
}

在使用上述代码时,在函数func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath)中,需要在cell上调用addAction,而不是在cell.deleteButton上调用:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PhotoCell", for: indexPath) as? PhotoCell else { return UICollectionViewCell() }

    let action = UIAction { _ in
        print("Delete button tapped!!!", indexPath.row)
    }
    cell.addAction(action, for: .touchUpInside)

    return cell
}
英文:

The actions get triggered multiple times because some instances of PhotoCell are reused by the UICollectionView. This means that the deleteButton of the PhotoCell returned by dequeueReusableCell() may already have actions added to it and will eventually possess multiple actions for the same events.

One possible solution is to override prepareForReuse() in PhotoCell and remove the actions there.

class PhotoCell: UICollectionViewCell {
    @IBOutlet weak var deleteButton: UIButton!
    var actions: [(UIAction, UIControl.Event)] = []

    func addAction(_ action: UIAction, for event: UIControl.Event) {
        self.actions.append((action, event))
        self.deleteButton.addAction(action, for: event)
    }

    override func prepareForReuse() {
        super.prepareForReuse()
        for tuple in self.actions {
            self.deleteButton.removeAction(tuple.0, for: tuple.1)
        }
        self.actions = []
    }
}

When using the above code addAction needs to be called on cell instead of cell.deleteButton in the function func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath):

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PhotoCell", for: indexPath) as? PhotoCell else { return UICollectionViewCell() }

    let action = UIAction { _ in
        print("Delete button tapped!!!", indexPath.row)
    }
    cell.addAction(action, for: .touchUpInside)

    return cell
}

答案2

得分: 1

一个可能的解决方案是在PhotoCell中覆盖prepareForReuse()方法,就像Johann所提到的那样。

> prepareForReuse() 用于执行任何必要的清理工作,以便为视图重新使用做准备。

我们可以在那里从按钮中移除所有事件!

class PhotoCell: UICollectionViewCell {
    @IBOutlet weak var deleteButton: UIButton!

    override func prepareForReuse() {
        super.prepareForReuse()
        self.deleteButton.removeTarget(nil, action: nil, for: .allEvents)
    }
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath)中像通常一样添加动作。

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PhotoCell", for: indexPath) as? PhotoCell else { return UICollectionViewCell() }
    let action = UIAction { _ in
        print("Delete button tapped!!!", indexPath.row)
    }
    cell.deleteButton.addAction(action, for: .touchUpInside)
    return cell
}
英文:

One possible solution is to override prepareForReuse() in PhotoCell, as Johann mentioned.

> preparaForReuse() is use to Performs any clean up necessary to
> prepare the view for use again.

We can remove all events from the button there!

class PhotoCell: UICollectionViewCell {
    @IBOutlet weak var deleteButton: UIButton!

     override func prepareForReuse() {
        super.prepareForReuse()
        self.deleteButton.removeTarget(nil, action: nil, for: .allEvents)
    }

}

Add action in func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) like normally we do.

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PhotoCell", for: indexPath) as? PhotoCell else { return UICollectionViewCell() }

        let action = UIAction { _ in
            print("Delete button tapped!!!", indexPath.row)
        }

        cell.deleteButton.addAction(action, for: .touchUpInside)

        return cell
    }

huangapple
  • 本文由 发表于 2023年3月31日 21:53:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/75899341.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定