I have created reusable cell with include a button in a XIB for a collection.
I can change the text of labels and buttons in the collection view but i can't get on click event.
I have tried below options:
a. This in UICollectionViewCell : Not working
class cellVC: UICollectionViewCell {
@IBOutlet weak var sampleLabel: UILabel!
@IBOutlet weak var buttonClicked: UIButton!
@IBAction func buttonTest(_ sender: Any) {
print("dsfsdf 111111")
}
}
b. I have also tried : Not working
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell : cellVC = collectionView.dequeueReusableCell(withReuseIdentifier: "cellVC", for: indexPath) as! cellVC
cell.sampleLabel. = "sample text"
cell.buttonClicked.tag = indexPath.row
cell.buttonClicked.addTarget(self, action: #selector(masterAction(sender:)), for: .touchUpInside)
cell.buttonClicked.backgroundColor = UIColor.cyan
return cell
}
func masterAction(_ sender: UIButton) {
print ("button click")
}
As both solutions are not working, how do i achieve this.
Thanks
thanks @Thomas If anyone wants the solution, the answer up top has been updated in swift :
instead of
write
Places To Check
There are several potential points of failure here, because what you're doing seems to look right. I have a sample Xcode project which has this working. (See it on GitHub.)
You can double check these things, to make sure everything is set up correctly:
It seems, based on what you've posted here that all of the above conditions are true, so let's talk a little more about what you're trying to do and see how all the pieces fit.
Why these Things Seem Ok
IBOutlets
are force-unwrapped by default. (And that's how your posted code works.)Implications of Cells and Recycling
In general, this is a tricky problem to solve, because of the way
UICollectionView
is designed. (The same applies toUITableView
.)Your button is inside of a re-usable collection view cell, which means that you not only have to handle taps, but you need to know which index path in your data set the button is currently referring to.
Further, views that are placed inside an instance of
UICollectionViewCell
are actually inside the collection view cell'scontentView
, which makes it just a little more work to handle.Finally, if your cells scroll on and offscreen, then you may end up attaching the action to your button multiple times as the cell is recycled. So, you need to account for that as well.
An Implementation Example
Let's work with a very simple custom cell, that has a single button in it:
The code for the button looks like this:
Really simple. Notice the
IBOutlet
. That needs to be wired up in interface builder as well.The last thing we need to do is set up the cell's reuse identifier so that we can access it in code. With the cell selected in Interface Builder, add an identifier to the cell:
Ok, that's all you need to do with the cell. We're ready to implement the view controller. In the view controller, we're going to set up a few things:
UICollectionViewController
instead of aUIViewController
, this won't be necessary.)Let's start with the
IBOutlets
:We need to wire those up in Interface builder. While you're at it, also connect the collection view's data source and delegate to be the "File's Owner" if they aren't connected already. (Again, if you're using a
UICollectionViewController
instead of aUIViewController
, this won't be necessary.)Next, let's implement some code to show our custom cell, using
UICollectionViewDataSource
. We need to show at least one section and at least one cell:When we dequeue the cell, we want to force downcast our dequeued cell to the custom cell class we defined earlier. Then we can access the update button to add an action to it.
Now we need to implement the method for accessing the index path of the cell:
This method handles all of the checks you need to handle the tap and determine which item was actually tapped.
sender
is actually aUIButton
instance. Not critical, but if we don't at least ensure it's aUIView
, we won't be able to get thesuperview
.superview
, which we assume to be thecontentView
.Notes
Edit:
@DonMag pointed out that you're using a nib instead of a storyboard, which I overlooked initially. So, here's the process for getting it all set up with nibs:
Let's start by making a new nib for the
CustomCollectionViewCell
. Using the "Empty" template, make a new nib called "CustomCollectionViewCell.xib." The name doesn't have to match the class, but it's going to be used later on, so to follow convention, let's call it that.In the new nib, drag out a collection view cell from the palette on the bottom right, and then add the label to it. Set the custom class to
CustomViewControllerCell
(or whatever yours is called) and connect the label to the outlet.Next, let's make a new View Controller. We probably can re-use the initial one, but so that we don't register two cells for the same identifier, let's stick to a new
UIViewController
subclass. To get the nib, check "Also create XIB file."Inside the new nib-based view controller, we want the same outlets as we had before. We also want the same UICollectionViewDataSource implementation. There's one thing that's different here: we need to register the cell.
To do so, let's add a method called
registerCollectionViewCell()
which we'll call fromviewDidLoad
. Here's what it looks like:Our
viewDidLoad()
method looks like this now:Inside the nib, lay out the label and collection view as we did before. Wire up the outlets and the collection view data source, and delegate, and we should be done!
A couple of other things:
application(_:didFinishLaunchingWithOptions:)
NSObject
. The cell should have a custom class matching whatever class you want to load.You can go with:
This sometimes happen when the button is linked to the File's Owner instead of your cell.
Right click on the button in IB and check if the referencing outlet is correct, this should be your cell and not the File's Owner like in the image below: