Reputation: 1999
I use a CollectionView
in my app, which of course has a CollectionViewCell
, In the CollectionViewCell
, I have a StackView
, which has buttons in it. When tapping one of the buttons, I call a method which needs to get the cell that the button belongs to. I tried getting the Sender
(the button) and then use Sender.superview
, but it gives me the StackView
, and Sender.superview.superview
didn't work. How can I get the current cell that the button is in?
Marked in Red, is the StackView
where the button is, and in Pink is the button I click.
Upvotes: 0
Views: 250
Reputation: 1902
You need a more scalable approach. I would add a closure var action: (() -> Void)?
to your custom CollectionViewCell
, and in collectionView(_:cellForItemAt:)
, assign the action. To avoid memory retain cycle, don't forget to unowned self
or weak self
. In your CollectionViewCell
, make an action function that would run action?()
.
So it would be like:
class CollectionViewCell: UICollectionViewCell {
var action: (() -> Void)?
@objc func buttonWasTouched(_ sender: UIButton) {
action?()
}
...
button.addTarget(self, action: #selector(buttonWasTouched), for: .touchUpInside)
...
}
and in your collectionView(_:cellForItemAt:)
:
cell.action = { [unowned self] in /* Do something with self and the cell. */ }
====
EDIT:
The following example demonstrates a minimal example of using the above approach:
class CollectionViewCell: UICollectionViewCell {
var action: (() -> Void)?
var votes = 0 {
didSet { button.setTitle("\(votes)", for: .normal) }
}
private lazy var button: UIButton = {
let button = UIButton()
// Set translatesAutoresizingMaskIntoConstraints to programmatically setup layout
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("\(votes)", for: .normal)
button.setTitleColor(.black, for: .normal)
button.addTarget(self, action: #selector(buttonWasTouched), for: .touchUpInside)
return button
}()
@objc private func buttonWasTouched(_ sender: UIButton) {
action?()
}
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(button)
// Programmatially setting layout constraints
NSLayoutConstraint.activate([
button.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
button.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
button.topAnchor.constraint(equalTo: contentView.topAnchor),
button.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
])
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class CollectionViewController: UICollectionViewController {
override func viewDidLoad() {
super.viewDidLoad()
collectionView.backgroundColor = .white
// Programatically register CollectionViewCell
collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "cell")
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
cell.action = { cell.votes += 1 }
return cell
}
}
If you run the code, each cells will have a button, which increments its number when touched.
Upvotes: 2