UITableViewCell Button is not Clickable IOS Swift - ios

So basically I am trying to do this:
I have a TableView that I load a custom cell (xib) into it. This custom cell has its own corresponding custom class to go with it. The cell loads just fine but the button is not clickable (at all, as in you don't even see any type of indication that the button was clicked at all). I did some googling, tried to set the action in the cell, tried using a delegate to handle the action. I am lost.
Here is the code from the files:
ViewController.swift
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Use your cell's reuse identifier and cast the result
// to your custom table cell class.
let cell = tableView.dequeueReusableCellWithIdentifier("ReserveTableViewCell", forIndexPath: indexPath) as! ReserveTableViewCell
cell.delegate = self
let button = cell.locationButton.viewWithTag(1) as? UIButton
button?.addTarget(self, action: #selector(ReserveTableViewController.loc(_:)), forControlEvents: .TouchUpInside)
cell.pickupDate.text = "Today"
cell.returnDate.text = "Tomorrow"
//cell.locationButton.addTarget(self, action: #selector(loc), forControlEvents: .TouchUpInside)
return cell
}
func loc(sender: UIButton!) {
print("here")
}
CustomCell.swift
import UIKit
protocol ReserveTableViewCellDelegate {
func pickupLocationClick(cell: ReserveTableViewCell)
}
class ReserveTableViewCell : UITableViewCell {
#IBOutlet weak var searchButton: UIButton!
#IBOutlet weak var compactIcon: UIImageView!
#IBOutlet weak var midsizeIcon: UIImageView!
#IBOutlet weak var suvIcon: UIImageView!
#IBOutlet weak var luxuryIcon: UIImageView!
#IBOutlet weak var sportIcon: UIImageView!
#IBOutlet weak var pickupIcon: UIImageView!
#IBOutlet weak var pickupDate: UILabel!
#IBOutlet weak var returnDate: UILabel!
#IBOutlet weak var locationButton: UIButton!
var delegate: ReserveTableViewCellDelegate?
#IBAction func locationButtonAction(sender: AnyObject) {
delegate?.pickupLocationClick(self)
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Some code is still left from other attempts (which is why the protocol method doesn't match the "loc" that is now defined in the viewcontroller.swift file.
In my TableView in storyboard I have selection turned off, when it is on and you tab on the cell it makes it all look "dim" so to speak.
I have the custom class set in my tableview and the outlets/actions are link in IB.
Anyone?

In my experience, it is possible to add functionality to the button in both the Cell file and in the ViewController file (which holds your TableView as a member)
ViewController.swift
#IBAction func locationButtonAction(sender: AnyObject) {
// Do Some Action Here (Which interacts with data members of View
}
CustomCell.swift
#IBAction func locationButtonAction(sender: AnyObject) {
// Do Another Action (Which interacts with data members of the cell)
}
Both of these functions can be linked to the TouchUpInside of the Cell button using the Storyboard Editor!
Best of luck!

Couple confusing things here...
let button = cell.locationButton.viewWithTag(1) as? UIButton
This means, hey locationButton, look in your subviews to find viewWithTag of 1. Not what you want. Just use
let button = cell.locationButton
Then you need to decide what action you're calling and what class does it live in. If you want the button to call an action in the ViewController class you add a target like:
button.addTarget(self, action: #selector(ViewController.loc(_:)), forControlEvents: .TouchUpInside)
If you want the button to call an action inside the ReserveTableViewController class, which honestly is pretty odd, you need a way to reference that class
button.addTarget(myReferenceToTheReserveTableViewControllerClass, action: #selector(ReserveTableViewController.loc(_:)), forControlEvents: .TouchUpInside)
What you may be actually looking for is for the button to call a method inside the cell class, in which you can then respond as necessary or delegate out to another class, which would then be:
button.addTarget(cell, action: #selector(ReserveTableViewCell.loc(_:)), forControlEvents: .TouchUpInside)

I have set up project just like you did. One thing i noticed in your in ViewController.swift is that you have written following code.
let button = cell.locationButton.viewWithTag(1) as? UIButton //If you will print or debug this button you will see nil
print("Button: \(button)") //prints "Button: nil"
button?.addTarget(self, action: #selector(ReserveTableViewController.loc(_:)), forControlEvents: .TouchUpInside)
Above code is not adding target for your Button. It is adding target for your Buttons subview which has tag value 1. Which is actually nil. Try following code.
cell.locationButton.addTarget(self, action: #selector(ReserveTableViewController.loc(_:)), forControlEvents: .TouchUpInside)(self, action: #selector(clicked), forControlEvents: .TouchUpInside)
Try the following code in your ViewController.swift
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Use your cell's reuse identifier and cast the result
// to your custom table cell class.
let cell = tableView.dequeueReusableCellWithIdentifier("ReserveTableViewCell", forIndexPath: indexPath) as! ReserveTableViewCell
cell.delegate = self
//let button = cell.locationButton.viewWithTag(1) as? UIButton
//button?.addTarget(self, action: #selector(ReserveTableViewController.loc(_:)), forControlEvents: .TouchUpInside)
cell.locationButton.addTarget(self, action: #selector(loc), forControlEvents: .TouchUpInside)
cell.pickupDate.text = "Today"
cell.returnDate.text = "Tomorrow"
return cell
}
func loc(sender: UIButton!) {
print("here")
}

Related

Swift 4.2: Unable to add custom behaviour to a custom UITableViewCell using Protocol oriented delegate concepts

I've a custom UITableViewCell, in that I've two UILabels & one UIButton. I'm able to load data...and display it as per requirement.
Problem Statement-1: Now problem exist in my UIButton, which is in my UICustomTableViewCell. Due to this I'm unable to handle click event on that UIButton.
Problem Statement-2: On button Click I have to identify the index of that Button click and pass data to next ViewController using segue.
Now have a look on...what did I've tried for this...
Yes, first-of-all I have thought that Binding IBOutlet action in my CustomCell will resolve my problem...but actually it doesn't solved my problem.
After that I've accessed button using .tag and initialised index path.row to it.
But it won't helped me.
So now I'm using Protocol oriented concept using delegate to handle click event on my UIButton which is available in CustomCell.
What did I tried:
SwiftyTableViewCellDelegate:
protocol SwiftyTableViewCellDelegate : class {
func btnAuditTrailDidTapButton(_ sender: LeadCustomTableViewCell)
}
CustomTableViewCell with delegate:
class LeadCustomTableViewCell: UITableViewCell {
#IBOutlet weak var lblMeetingPersonName: UILabel!
#IBOutlet weak var lblPolicyNo: UILabel!
#IBOutlet weak var btnLeadAuditTrail: UIButton!
weak var delegate: SwiftyTableViewCellDelegate?
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
#IBAction func btnAuditTrailTapped(_ sender: UIButton) {
delegate?.btnAuditTrailDidTapButton(self)
}
}
ViewController implementing delegate:
class LeadViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, SwiftyTableViewCellDelegate {
//IBOutlet Connections - for UITableView
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
//setting dataSource & delegates of UITableView with this ViewController
self.tableView.dataSource = self
self.tableView.delegate = self
//Reloading tableview with updated data
self.tableView.reloadData()
//Removing extra empty cells from UITableView
self.tableView.tableFooterView = UIView()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:LeadCustomTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "cell") as! LeadCustomTableViewCell
//Assigning respective array to its associated label
cell.lblMeetingPersonName.text = (meetingPersonNameArray[indexPath.section] )
cell.lblPolicyNo.text = (String(policyNoArray[indexPath.section]))
cell.btnLeadAuditTrail.tag = indexPath.section
cell.delegate = self
return cell
}
//This is delegate function to handle buttonClick event
func btnAuditTrailDidTapButton(_ sender: LeadCustomTableViewCell) {
guard let tappedIndexPath = tableView.indexPath(for: sender) else { return }
print("AuditTrailButtonClick", sender, tappedIndexPath)
}
Don't know why this is not working.
Link the touch up inside event in cellForRow by adding the following code:
cell.btnLeadAuditTrail.addTarget(self, action:#selector(btnAuditTrailDidTapButton(_:)), for: .touchUpInside)

UIButton inside table not triggering

Hello I'm trying to figure out how to call a UIButton inside a custom cell within a UItable in storyboard. At the moment I have a library that creates a sidemenu working just fine (more info here) and I can see the button I placed when I launch the simulator. However, when I click on the button the action is not triggered, can you please guide me as to how I can achieve this?
Important to note that the table was create entirely in storyboard.
My work in progress code within TopratedVC.swift to get the button to trigger the action:
override func viewDidLoad() {
super.viewDidLoad()
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("UITableViewVibrantCell") as! CellClassMenu
cell.sendFeedBackBtn.tag = indexPath.row
cell.sendFeedBackBtn.addTarget(self, action: "sendFeedBackBtnAction:", forControlEvents: .TouchUpInside)
cell.contentView.userInteractionEnabled = false //tried with true as well, no difference
cell.bringSubviewToFront(cell.sendFeedBackBtn)
cell.userInteractionEnabled = true
return cell
}
func sendFeedBackBtnAction(sender: UIButton){
print("sendFeedBackBtnAction tapped")
}
My UITableViewVibrantCell.swift file contains the following:
import UIKit
class UITableViewVibrantCell: UITableViewCell {
#IBOutlet var sendFeedBackBtn: UIButton!
}
My sndFeedBackBtn has a referencing outlet to UITableViewVibrantCellsendFeedBackBtn which has a class of UITableViewVibrantCell. What am I doing wrong? Thank you.
What it looks like in simulator:
In your post, you show a UITableViewVibrantCell class, and dequeue a cell with the "UITableViewVibrantCell" identifier, but cast it as CellClassMenu?
Anyhow, it would be better practice to create a cell delegate for actions, and let your controller decide the implementation, rather than adding a target every time the cell is dequeued. You can do that like so:
UITableViewVibrantCell
import UIKit
protocol UITableViewVibrantCellDelegate: NSObjectProtocol {
func buttonPressed(sender: UIButton)
}
class UITableViewVibrantCell: UITableViewCell {
var delegate: UITableViewVibrantCellDelegate?
#IBOutlet var feedbackButton: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
feedBackButton.addTarget(self, action: #selector(self.buttonPressed(_:)), forControlEvents: .TouchUpInside)
}
func buttonPressed(sender: UIButton) {
delegate?.buttonPressed(sender)
}
}
TopratedVC
class TopratedVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("UITableViewVibrantCell") as! UITableViewVibrantCell
cell.delegate = self
return cell
}
// MARK: - UITableViewVibrantCellDelegate
func buttonPressed(sender: UIButton) {
print("feedbackButton tapped")
}
}
Selectors ("sendFeedBackBtnAction:") can't pass parameters. And a param isn't needed in the sendFeedBackBtnAction function since you're calling it only for this button. So change I'd change it to simply...
func sendFeedBackBtnAction()
then I'd also recommend changing your selector to a more updated swift version...
cell.sendFeedBackBtn.addTarget(self, action: #selector(sendFeedBackBtnAction), forControlEvents: .TouchUpInside)

Hiding buttons in cells

I am developing app which users will choose one of the two pictures in one cell. My prototype cell looks like :
and I have
cell.rightVoteButton.addTarget(self, action: #selector(voteRightButtonPressed), forControlEvents: .TouchUpInside)
cell.leftVoteButton.addTarget(self, action: #selector(voteLeftButtonPressed), forControlEvents: .TouchUpInside)
in the tableView function.
func voteRightButtonPressed(sender:UIButton){
print("Right Vote clicked is \(sender.tag)")
print(self.polls[sender.tag].poll_id)
}
in this way I am printing the polls id and cells index.
I want to hide the Vote button after clicking specific cell.
For example if I click the vote in first cell and the left picture, I want to hide the two buttons on the first cell. I now the cell's index but how can hide the buttons in specific cell.
My Custom TableViewCell:
class CustomTableViewCell: UITableViewCell {
#IBOutlet weak var leftImage: UIImageView!
#IBOutlet weak var rightImage: UIImageView!
#IBOutlet weak var userPicture: UIImageView!
#IBOutlet weak var userName: UILabel!
#IBOutlet weak var leftButton: UIButton!
#IBOutlet weak var rightButton: UIButton!
#IBOutlet weak var pollDescription: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Rather than using tags, which have no intrinsic meaning and are easy to confuse, you can say something like:
func voteRightButtonPressed(sender:UIButton){
let location = self.tableView.convertPoint(sender.bounds.origin, fromView:sender)
let indexPath = self.tableView.indexPathForRowAtPoint(location)
if let cell = self.tableView.cellForRowAtIndexPath(indexPath) as? CustomUITableViewCell {
//hide views in cell and update your model to reflect vote.
}
print("Right Vote clicked is \(indexPath.row)")
print(self.polls[indexPath.row].poll_id)
}
Once you have the cell you can hide the views you want and you can update your model to reflect the vote just cast.
I suppose you have a tableview with several cells of this type, if this is the case you need a custonTableViewCell where you put this code. the buttons respond to a #IBAction that hide the buttons and call a delegate in his UITableViewController.
understand your custonTableViewCell like a viewController for each cell
add this in your custon cell and point the button
// IBAction for buttons
#IBAction func buttonPress(sender: UIButton){
leftButton.hidden = true
rightImage.hidden = true
self.delegate?.buttonPress(sender.tag)
}
add this before your custon cell class definition
protocol CustomTableViewCellDelegate {
func buttonPress(tag: Int)
}
and this after:
var delegate : CustomTableViewCellDelegate?
and in your tableviewcontroller
class TableViewController: UITableViewController, CustomTableViewCellDelegate
and your new func in some place
func buttonPress(tag: Int){
print(tag)
}

Why is the button not experiencing the click?

I have a button in a custom cell, and it is not doing anything when I click on it. I tried mistyping the addTarget method on purpose so it will crash, so I can get conformation that the button is getting called, but the app won't crash. Why is nothing happening? Below is the code for the custom cell and the tablecell.
class ProfileMusicCell: UITableViewCell {
#IBOutlet weak var customtitle: UILabel!
#IBOutlet weak var customartist: UILabel!
#IBOutlet weak var playbutton: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
Below is the code for the table view
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = table.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! ProfileMusicCell
cell.customtitle.text = ret[indexPath.row]
cell.customartist.text = ter[indexPath.row]
cell.customtitle.font = UIFont(name: "Lombok", size: 22)
cell.customtitle.textColor = UIColorFromRGB("4A90E2")
cell.customartist.font = UIFont(name: "Lombok", size: 16)
cell.customartist.textColor = UIColor.blackColor()
cell.playbutton.tag = indexPath.row
//I am purposely leaving out the : so the app can crash, but it is not crashing.
cell.playbutton.addTarget(self, action: "playmymusic", forControlEvents: .TouchUpInside)
}
func playmymusic(sender: UIButton!) {
let playButtonrow = sender.tag
print(ret[playButtonrow])
print(ter[playButtonrow])
}
I think the issue is that you're calling the playmymusic without the colon. The colon is needed because of the sender parameter.
The compiler is trying to call a method with this name:
func playmymusic()
instead of the real one:
func playmymusic(sender: UIButton!)
So just try to add the colon (:) at the end of action like this:
cell.playbutton.addTarget(self, action: "playmymusic:", forControlEvents: .TouchUpInside)
This is a legacy of Objective-c way to call the methods (send the messages) and I know it's a little bit confusing.

Passing index path information to button action from cell Swift

I have this:
let mapsbut = cell.viewWithTag(912) as! UIButton
mapsbut.addTarget(self, action: "mapsHit:", forControlEvents: UIControlEvents.TouchUpInside)
and
func mapsHit(){
// get indexPath.row from cell
// do something with it
}
How is this accomplished?
You can always use the tag from your button to pass or hold a value, or a var inside of your custom cell implementation. For example, if you have your button as an outlet in your UITableViewCell (for instance):
class MenuViewCell: UITableViewCell {
#IBOutlet weak var titlelabel: UILabel!
#IBOutlet weak var button: UIButton! {
didSet {
button.addTarget(self, action: "mapsHit:", forControlEvents: UIControlEvents.TouchUpInside)
}
}
func mapsHit(sender: UIButton){
let indexPathOfThisCell = sender.tag
println("This button is at \(indexPathOfThisCell) row")
// get indexPath.row from cell
// do something with it
}
}
Notice here, that you need to set sender:UIButton as a parameter when you set "mapsHit:". This will be the button itself in wich the user has tapped.
Now, for this to work, your tag can not be "912". Instead when you build your cell, assign to the tag property of your button, the value of it's indexPath.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MenuViewCell", forIndexPath: indexPath) as! MenuViewCell
cell.titlelabel?.text = data[indexPath.row].description
cell.button.tag = indexPath.row
return cell
}
...one solution could be to have a var inside your class holding the indexPath of the last cell tapped, then you can use that value inside your mapsHit() function.

Resources