Figure out which of multiple buttons in UICollectionView cell was tapped - ios

In my Swift code, I have a UICollectionViewCell with 3 buttons (all three have IBActions). From my UICollectionViewController I now want to "catch" the individual button taps.
I've followed this StackOverflow question and I can catch the UICollectionViewCell's touch-up inside up in my CollectionViewController with adding this line to the viewDidLoad
gestureRecognizer.cancelsTouchesInView = false
and with this function
func handleTapForCell(recognizer: UITapGestureRecognizer){
//I can break in here
}
But the piece missing now is how can I figure out which of the three buttons have been tapped? I have set different tags on the buttons but I have not found any place on the gestureRecognizer dealing with these tags.
Any ideas?

I think, you don't need to add Gesture on cell to get a button action of a tableviewCell. This code may help you:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//Your tableviewCell code here
//set tag of cell button
cell.button1.tag = 1
cell.button2.tag = 2
cell.button3.tag = 3
//add action of your cell button
cell.button1.addTarget(self, action: Selector("cellButtonTapped:event:"), forControlEvents: .TouchUpInside)
cell.button2.addTarget(self, action: Selector("cellButtonTapped:event:"), forControlEvents: .TouchUpInside)
cell.button3.addTarget(self, action: Selector("cellButtonTapped:event:"), forControlEvents: .TouchUpInside)
// return cell
}
func cellButtonTapped(sender:UIButton, event:AnyObject){
let touches: NSSet = event.allTouches()!
let touch = touches.anyObject()
let currentTouchPosition: CGPoint = (touch?.locationInView(YOUR_TABLEVIEW_INSTANCE))!
if let indexPath: NSIndexPath = self.YOUR_TABLEVIEW_INSTANCE.indexPathForRowAtPoint(currentTouchPosition)!{
if sender.tag == 1{
//cell first button tap
}else sender.tag == 2{
//cell second button tap
}
else sender.tag == 3{
//cell 3rd button tap
}
}
}

You can follow the protocol/delegate paradigm.
What you need to do is define a protocol in Custom cell. Then make the viewcontroller subscribe to the cell delegate.
Implement the IBActions inside the custom cell class. Call the delegate methods in the IBActions of the buttons. viewcontroller who is delegating for the cell will receive the callbacks for button taps inside the cell.

Related

Detecting Tap Events for UILabel inside UITableViewCell using UITapGestureRecognizer

I have a UITableViewDataSource with the following
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: OutletDetails.cellIdentifier) as! OutletDetails
cell.selectionStyle = .none
cell.isUserInteractionEnabled = true
cell.location = "Some location will be here"
let tap = UITapGestureRecognizer(target: self, action: #selector(locationClicked))
tap.cancelsTouchesInView = false
tap.numberOfTapsRequired = 1
cell.location.addGestureRecognizer(tap)
}
where cell.location is a UILabel object. What I'm trying to do here is to detect tap events on the UILabel. I looked all over the Internet and everyone is suggesting this method, however, this code is not working in my case. The method locationClicked is not being called at all. Can anyone tell me what's wrong with my code?
Edit
One more thing, is it a good idea to do it this way memory-wise? I mean if we have a long list, then many UIGestureRecognizer objects will be generated for each cell. This is because the method will be called a lot while scrolling the items.
Add tap gesture to the object and enable its user interaction. Yes you can take button as well.
//Adding tap gesture
let cellNameTapped = UITapGestureRecognizer(target: self, action: #selector(nameTapped))
nameLabel.isUserInteractionEnabled = true// UILabel made available for touch interaction
nameLabel.addGestureRecognizer(cellNameTapped) //gesture added
//Method called on touch of nameLabel
#objc func nameTapped(tapGestureRecognizer: UITapGestureRecognizer){
//print(tapGestureRecognizer.view)
}
Since you're dequeuing a cell, you will need to somehow get a reference to the UITapGestureRecognizer and either remove it or reuse it. Otherwise every time you reuse a cell, you will be laying another recognizer onto the one that is already on the cell. If you're subclassing the UITableViewCell you can just add the recognizer as a property.
However, using the code you posted I'm suggesting you use a UIButton and add a tag so you can get a reference to it later. You can set the bounds of the button equal to the bounds of the label. Try something like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: OutletDetails.cellIdentifier) as! OutletDetails
cell.selectionStyle = .none
cell.isUserInteractionEnabled = true
cell.location = "Some location will be here"
// If we don't already have a button on our cell, create one and set the tag
if cell.viewWithTag(103) as? UIButton == nil {
let newButton = UIButton(frame: cell.location.bounds)
newButton.tag = 103
newButton.addTarget(target: self, action: #selector(locationClicked), for: .touchUpInside)
}
}

addTarget for button in tableView

I'm trying to add the download button for some items in my tableView. I've created the custom cell class and added the label and the button outlets, everything is working in displaying the info and even the buttons are showing where it should be.
I'm trying to add the target, but it does nothing. I need to pass the row index to the buttonClicked function or should I create this function in the custom cell class and then do the action some how? I would like to know the best practise of this.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PlaylistCell", for: indexPath) as! PlaylistTableViewCell
let playlist = self.playlists?[indexPath.row]
cell.titleLabel.text = playlist?.getTitle()
if (playlist?.isOfflineAvailable())! {
cell.downloadButton.isHidden = false
} else {
cell.downloadButton.isHidden = true
cell.downloadButton.tag = indexPath.row
cell.downloadButton.addTarget(self, action: #selector(buttonClicked(sender:)), for: .touchUpInside)
}
return cell
}
func buttonClicked(sender: UIButton) {
let buttonRow = sender.tag
print(buttonRow)
}
I've also tried removing the (sender:) from #selector, but it does not change a functionality.
In order to handle button callback in your view controller, you have two choices:
Target-action:
Add target-action in cellForRow method just as you did. Your code is probably not working because you are hiding the button when it should be visible, aren't you?
I guess you need to replace this
if (playlist?.isOfflineAvailable())! {
cell.downloadButton.isHidden = false
} else {
cell.downloadButton.isHidden = true
cell.downloadButton.tag = indexPath.row
cell.downloadButton.addTarget(self, action: #selector(buttonClicked(sender:)), for: .touchUpInside)
}
With this:
cell.downloadButton.isHidden = playlist?.isOfflineAvailable()
cell.downloadButton.tag = indexPath.row
cell.downloadButton.addTarget(self, action: #selector(buttonClicked(sender:)), for: .touchUpInside)
You should update tag every time because cell are reused in tableView and if don't do it every time when cellForRow is called, you may easilly get a case when a callback is called but it's tag belongs to indexPath from the previous cell usage. Also I've changed isHidden logics to the opposite. I guess you should hide the button when isOfflineAvailable returns true, right?
Delegate pattern
It is described a million of times here on SO and on many other sites as well. Basically you define a cell protocol, implement it in your controller and send callbacks from cell to it's delegate whenever a button is pressed. You can find more details in my answer for a similar question.

How to tap buttons or images without selecting the whole cell with Swift?

I'm using a custom TableViewCell in my iOS app. I use the method tableViewDidSelectRowAtIndexPath to open a new ViewController. What I need to do is to add a button or an image somewhere in the custom cell so if I tap the button or whatever element don't open the ViewController, but execute a function without opening the cell.
set [cell.button setTag:indexPath.row] in cellForRowAtIndexPath method.
and than addTarget to cell.button like
[cell.button addTarget:self action:#selector(yourAction:) forControlEvents:UIControlEventTouchUpInside]];
and than do Whatever you want to do in yourAction
with getting tag from sender.
Or you want code for that than please add your code what you had done so we can help more if you are new in iOS.
This code may helps you
here i have used custom buttom in table and add target to that buton
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell: AnyObject = tableView.dequeueReusableCellWithIdentifier("CellIdentifier", forIndexPath: indexPath)
// use your custom cell here
//cell = UIColor.redColor()
//cell.textLabel?!.text = String(data[indexPath.row])
//nameTextField.text = ""
let custom_btn : UIButton? = UIButton.init(type: .System)
//declaring custom button
custom_btn?.setTitle("custom button", forState: .Normal)
custom_btn!.tag = indexPath.row
custom_btn!.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
cell .addSubview(custom_btn!);
return cell as! UITableViewCell
}
func buttonClicked(sender:UIButton)
{
if(sender.tag == 5){
//Do something for tag
}
print("hello")
}

Implementing accessoryButtonTappedForRowWithIndexPath: in Swift 2

I'm attempting to implement accessoryButtonTappedForRowWithIndexPath: in Swift 2 on a UITableViewController.
As I explain below, I think I'm missing something in when I create the disclosureIndicator, but I don't know what. It gets drawn from code, but my target action doesn't get called. UGH!
To do this programmatically, my understanding is I need to add the detailDisclosure indicator in cellForRowAtIndexPath before my cell is returned. I'm doing that as follows:
// Create disclosure indicator button in the cell
let disclosureIndicatorButton = UIButton(type: UIButtonType.DetailDisclosure)
disclosureIndicatorButton.addTarget(self, action: "disclosureIndicatorPressed:event:", forControlEvents: UIControlEvents.TouchUpInside)
customCell.accessoryType = .DisclosureIndicator
In my code, the detailDisclosure chevron gets drawn, but the target action method I assigned to it doesn't get called.
Then I need to create a handler for the button when it's pressed:
func disclosureIndicatorPressed(sender: UIButton, event: UIControlEvents) {
print("disclosure button pressed")
// convert touches to CGPoint, then determine indexPath
// if indexPath != nil, then call accessoryButtonTappedForRowWithIndexPath
}
Finally accessoryButtonTappedForRowWithIndexPath contains code to perform the segue, which I can do. What am I missing?
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableViewDelegate_Protocol/#//apple_ref/occ/intfm/UITableViewDelegate/tableView:accessoryButtonTappedForRowWithIndexPath:
Not sure why you are adding disclosure button indicator like that in code.
What you are looking for is simply 2 step process -
Step 1 : Add correct accessoryType on cell:
cell.accessoryType = .DetailDisclosureButton
Step 2 : Take action when the button is tapped by creating the accessoryButtonTappedForRowWithIndexPath function:
override func tableView(tableView: UITableView, accessoryButtonTappedForRowWithIndexPath indexPath: NSIndexPath) {
doSomethingWithItem(indexPath.row)
}

iOS 9: Gesture Recognizer was setup in a storyboard/xib to be added to more than one view (not working)

Using iOS 9 and facing a problem with a UITapGestureRecognizer. I have a ViewController-A with a UITableView. I have added a tableViewCell which has a textLabel. I want to implement tap on the textLabel. So if I tap on textLabel -- it should print on Console or do anything else
Issue: TapRecogniser is not working. Getting the below error:
Following is what I have done:
1) Added a `UITapGestureRecognizer' on the textLabel (From StoryBoard). Enabled User Interaction for the textLabel (the error even now)
2) Following is the IBAction:
#IBAction func nameTap(sender: UITapGestureRecognizer) {
print("a")
}
3) CellForRowAtIndexPath
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as! ThirdViewCell!
cell.nameLabel?.text = "XYZ"
let nameTapRecognizer = UITapGestureRecognizer(target: self, action: Selector("nameTap:"))
nameTapRecognizer.cancelsTouchesInView = false
cell.nameLabel?.addGestureRecognizer(nameTapRecognizer)
return cell
}
P.S:
1) This was working in iOS 8. I have checked..There are no duplicates (there is only one tap recognizer in the entire file and its linked to the textLabel)
2) I don't want to use didSelectRowAtIndexPath method as I need to implement TapGestureRecognizer for more textLabels within the tableViewCell.
are you see the error console Label, and the property as UserInteractionEnabled = NO; see the screen shot
try this
let nameTapRecognizer = UITapGestureRecognizer(target: self, action: Selector("nameTap:"))
nameTapRecognizer.cancelsTouchesInView = false
cell.nameLabel?.tag = indexPath.row // add this
nameTapRecognizer.numberOfTapsRequired = 1 // add this
nameTapRecognizer.delegate =self
cell.nameLabel?.userInteractionEnabled = true // add this
cell.nameLabel?.addGestureRecognizer(nameTapRecognizer)
// method
func nameTap(gesture: UITapGestureRecognizer) {
let indexPath = NSIndexPath(forRow: gesture.view!.tag, inSection: 0)
let cell = tableView.cellForRowAtIndexPath(indexPath) as UITableViewCell
// Do whatever you want.
}

Resources