Lets say I have 10 buttons in a custom UITableViewCell. How do I identify which button is tapped and perform the respective action back in ViewController which holds the cell? I am looking for a optimistic solution in swift. Thank you
//Put this code in your UITableViewDataSource: cellForRowAt
//Cell must contains these buttons on which you need add do addTarget
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "currentTableViewCell", for: indexPath)
as! CurrentTableViewCell
cell.btn1.tag = 1
cell.btn1.addTarget(self, action: #selector(buttonPressed(sender:)), for: .touchUpInside)
cell.btn2.tag = 2
cell.btn2.addTarget(self, action: #selector(buttonPressed(sender:)), for: .touchUpInside)
cell.btn3.tag = 3
cell.btn3.addTarget(self, action: #selector(buttonPressed(sender:)), for: .touchUpInside)
return cell
}
#objc func buttonPressed(sender: UIButton) {
let convertedPointInTable = sender.convert(CGPoint.zero, to:self.currentTableView)
let retriveIndexPath = self.currentTableView.indexPathForRow(at: convertedPointInTable)
print("In which cell \(retriveIndexPath!.row), which button pressed \(sender.tag)")
}
Cell code
final class MyCell: UITableViewCell {
struct ConfiguringData {
let action1: () -> Void
let action2: () -> Void
}
#IBOutlet private weak var button1: UIButton!
#IBOutlet private weak var button2: UIButton!
private var didTapButton1Action: (() -> Void)?
private var didTapButton2Action: (() -> Void)?
#IBAction private func didTapButton1() {
didTapButton1Action?()
}
#IBAction private func didTapButton2() {
didTapButton2Action?()
}
func configure(with configuringData: ConfiguringData) {
didTapButton1Action = configuringData.action1
didTapButton2Action = configuringData.action2
}
}
ViewController code
class MyViewController: UIViewController {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCell(withIdentifier: "MyCell") as! MyCell
let action1 = { print("didTapButton1") }
let action2 = { print("didTapButton2") }
myCell.configure(
with: MyCell.ConfiguringData(action1: action1, action2: action2)
)
return myCell
}
}
**There two ways to do that =:
First you can make collection of IB button outlets and define tag.
Second One is same but instead of making collection of outlets, go to your storyboard and on click button, you will see tag option there, for each button give different tag(suppose if you have 10 button give tag 1 to 10)
Since you are doing it under custom cell, you can either do above things inside UITableViewCell class or you can preform target under cellForRowAt also, both will work fine
// Now inside your button action do this -:
if sender.tag == 1{
print("one")
}
if sender.tag == 2{
print("two")
}
//OR (if inside cellForRowAt)
cell.button.addTarget(self, action:#selector(handleRegister), for: .touchUpInside)
*so on...
//Make sure sender type in IBAction must be UIButton
// you can also put this under switch cases, just basic programming*
Related
I am trying to manage two buttons in same custom tableview cell.
Added two buttons named Yes and No. If yes button is selected the No button will be inactive and Yes button became active.
Here is the image what I need
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableCell") as! TableViewCell
cell.yesButton.tag = 101
cell.noButton.tag = 102
cell.yesButton.addTarget(self, action: #selector(buttonClicked(sender:)), for: UIControl.Event.touchUpInside)
cell.noButton.addTarget(self, action: #selector(buttonClicked(sender:)), for: UIControl.Event.touchUpInside)
return cell
}
#objc func buttonClicked(sender: AnyObject) {
let buttonPosition = (sender as AnyObject).convert(CGPoint.zero, to: tableList)
let indexPath = tableList.indexPathForRow(at: buttonPosition)
if sender.tag == 101 {
if indexPath != nil {
print("Cell indexpath = \(String(describing: indexPath?.row))")
}
}
if sender.tag == 102 {
if indexPath != nil {
print("Cell indexpath = \(String(describing: indexPath?.row))")
}
}
}
Create a model to main the state of yesButton and noButton for each tableViewCell, i.e.
class Model {
var isYesSelected = false
var isNoSelected = false
}
Create a custom UITableViewCell with Outlets of yesButton and noButton.
Create a single #IBAction for both the buttons and handle their UI based on which button is tapped.
Also, use a buttonTapHandler to identify the row in which the button is tapped. It will be called everytime a button is tapped. We'll be setting this when creating the instance of TableViewCell in tableView(_:cellForRowAt:).
class TableViewCell: UITableViewCell {
#IBOutlet weak var yesButton: UIButton!
#IBOutlet weak var noButton: UIButton!
var buttonTapHandler: (()->())?
var model: Model?
override func prepareForReuse() {
super.prepareForReuse()
yesButton.backgroundColor = .gray
noButton.backgroundColor = .gray
}
func configure(with model: Model) {
self.model = model
self.updateUI()
}
#IBAction func onTapButton(_ sender: UIButton) {
model?.isYesSelected = (sender == yesButton)
model?.isNoSelected = !(sender == yesButton)
self.updateUI()
}
func updateUI() {
yesButton.backgroundColor = (model?.isYesSelected ?? false) ? .green : .gray
noButton.backgroundColor = (model?.isNoSelected ?? false) ? .green : .gray
}
}
UITableViewDataSource's tableView(_:cellForRowAt:) method goes like,
let numberOfCells = 10
var models = [Model]()
override func viewDidLoad() {
super.viewDidLoad()
(0..<numberOfCells).forEach { _ in
self.models.append(Model())
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numberOfCells
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath) as! TableViewCell
cell.configure(with: models[indexPath.row])
cell.buttonTapHandler = {
print(indexPath.row)
}
return cell
}
To get the totalPoints, count the models with isYesSelected = true, i.e.
let totalPoints = models.reduce(0) { (result, model) -> Int in
if model.isYesSelected {
return result + 1
}
return 0
}
print(totalPoints)
Get that Button using your Tag like below and after that, you can change the value as per you want.
var tmpButton = self.view.viewWithTag(tmpTag) as? UIButton
Simple 3 step process...!!
Define Model Class
Prepare tableView Cell & handle actions
Set up tableView in view controller
Let's start implementation:
1) Define Model Class
In UI, we have a information like question & it's answer (Yes/No). So design model respectively.
//MARK:- Class Declaration -
class Question {
let questionText: String
var answerState: Bool?
init(question: String) {
self.questionText = question
}
}
2. Prepare tableView Cell & handle actions
Create a custom tableView cell with Question Label, Yes Button & No Button. Link that view with respected #IBOutlets & #IBActions.
import UIKit
class TableViewCell: UITableViewCell {
#IBOutlet weak var questionLabel: UILabel!
#IBOutlet weak var yesButton: UIButton!
#IBOutlet weak var noButton: UIButton!
var question: Question?
var toggle: Bool? {
didSet {
question?.answerState = toggle
//Do buttons operations like...
if let isToggle = toggle {
yesButton.backgroundColor = isToggle ? .green : .gray
noButton.backgroundColor = isToggle ? .gray : .green
} else {
yesButton.backgroundColor = .gray
noButton.backgroundColor = .gray
}
}
}
func prepareView(forQuestion question: Question) {
self.question = question
questionLabel.text = question.questionText
toggle = question.answerState
}
//Yes Button - IBAction Method
#IBAction func yesButtonTapped(_ sender: UIButton) {
toggle = true
}
//No Button - IBAction Method
#IBAction func noButtonTapped(_ sender: UIButton) {
toggle = false
}
}
3. Set up tableView in view controller
class ViewController: UIViewController {
//Prepare questions model array to design our tableView data source
let arrQuestions: [Question] = [Question(question: "Do you speak English?"), Question(question: "Do you live in Chicago?")]
}
//MARK:- UITableView Data Source & Delegate Methods -
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrQuestions.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let tableViewCell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell") as? TableViewCell else {
return UITableViewCell()
}
tableViewCell.prepareView(forQuestion: arrQuestions[indexPath.row])
return tableViewCell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 80.0
}
}
Create basic tableView and configure dataSource functions
Create tableView cell with two buttons
Create cell class with buttons outlets and actions
Result of this code
Enjoy!
I have a checkbox (UIButton) and a label in a UITableViewCell. I want to change the label's text (color + strikethrough) when I click on the checkbox.
This is for a Recipe Application. After a cooking step is done, the user can "check" it as done.
This is my current cellForRowAt Function for the tableView:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == groceryTableView {
let cell = tableView.dequeueReusableCell(withIdentifier: groceryTableViewCell, for: indexPath) as! GroceryItemTableViewCell
cell.amoutLabel.text = indexPath.item % 2 == 0 ? "50 g" : "500 ml"
cell.itemLabel.text = indexPath.item % 2 == 0 ? "Cheese" : "Milk"
cell.selectionStyle = .none
return cell
}
else {
let cell = tableView.dequeueReusableCell(withIdentifier: cookingStepTableViewCell, for: indexPath) as! CookingStepTableViewCell
cell.cookingStepDescription.text = indexPath.item % 2 == 0 ? "Test 123..." : "Test 321..."
cell.selectionStyle = .none
cell.delegate = self
return cell
}
}
And this is my Button addTarget Function, which is delegated from the TableViewCell Class to the actual ViewController Class:
func cookingStepDone(description: String, isDone: Bool) {
// if isDone == true
// label textcolor is gray + strikethrough
// if isDone == false
// no change...
}
I want that cell.cookingStepDescription label is changed if "isDone" is true (= click on the checkbox)
Assuming that the button outlet is taken in cell class. so declare a action method in the cellForRowAtIndexpath i.e like this.
cell.yourDoneBtn?.addTarget(self, action: #selector(self.cookingStepDone), for: .touchUpInside)
Now in your action function:
#objc func cookingStepDone(sender: UIButton)
{
let location = self.yourTableViewName?.convert(sender.bounds.origin, from:sender)
let indexPath = self.yourTableViewName?.indexPathForRow(at: location!)
if let cell = self.yourTableViewName.cellForRow(at: indexPath!) as? yourTableViewCell // i.e groceryTableViewCell or CookingStepTableViewCell
{
if isDone == true
{
// Set your cell label textcolor to gray + strikethrough
}
else
{
// no change
}
}
DispatchQueue.main.async
{
self.yourTableView.reloadData() // reload your table view
}
}
Set your bool value where ever needed.
You can do this using below approach
define an Array, in you cookingStepDone method add indexPath to the array and if indexPath already in Array remove it and reload the tableView. and in cellForRowAtIndexpathmethod, check if the Array contains the indexPath. if contains make text strikeThrough else make normal.
What if you create a new class whose superclass would be UITableViewCell and inside that class you add in your #IBOutlets (UIButton and UILabel) and an #IBAction (buttonWasTapped)?
Something Like:
class RecipeTableViewCell: UITableViewCell {
#IBOutlet var myButton : UIButton!
#IBOutlet var myLabel : UILabel!
#IBAction func didTouchButton(sender : UIButton)
{
myLabel.textColor = UIColor.green;
}
}
Checkout this code : RecipeTableViewCell
class RecipeTableViewCell: UITableViewCell {
#IBOutlet var myButton : UIButton!
#IBOutlet var myLabel : UILabel!
var buttonClick : (() -> Void)? = nil
override func awakeFromNib() {
myButton.addTarget(self, action: #selector(didTouchButton(sender:)), for: .touchUpInside)
}
#IBAction func didTouchButton(sender : UIButton)
{
if let action = buttonClick {
action()
}
}
}
In cellForRowAt
let cell = tableView.dequeueReusableCell...
// Your code ...
cell.buttonClick = {
//access your label and data from here
cell.yourLbl.text = yourModel[indexPath.row].text
}
I have a function inside a protocol that takes a TableViewcell as an argument.
protocol GoingButtonDelegate {
func goingButtonPressed(cell: TableViewCell)
}
class TableViewCell: UITableViewCell {
// Outlets
#IBOutlet weak var goingButton: UIButton!
var delegate: GoingButtonDelegate?
#IBAction func goingButtonTapped(_ sender: Any) {
delegate?.goingButtonPressed(cell: self)
}
I then go over to my ViewController and implement the delegate and it's function, which is to change the image of a button when tapped. The "goingSelected" is a green image and the "goingDeselected" is a red image.
This all works out fine, when tapped the button of a cell goes from red to green and vice versa. However, when the cell gets reused, the button state of the cell persists and gets reused for the new row that enters view. Is there any way to stop this from happening?
extension ViewController: GoingButtonDelegate {
func goingButtonPressed(cell: TableViewCell) {
cell.goingButton.isSelected = !cell.goingButton.isSelected
if cell.goingButton.isSelected == true {
cell.goingButton.setImage(UIImage(named: "goingSelected"), for: UIControlState.selected)
} else if cell.goingButton.isSelected == false {
cell.goingButton.setImage(UIImage(named: "goingDeselected"), for: UIControlState.normal)
}
}
}
It's possible
just replace
let cell = tableView.dequeueReusableCell(withIdentifier: identifier,
for: indexPath)
with:
let cell= Bundle.main.loadNibNamed(identifier, owner: self, options: nil)?[0]
but I think you need to change your app logic.
Set Images inside of your cell class
class TableViewCell: UITableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
self.goingButton.setImage(UIImage(named: "goingDeselected"), for:.normal)
self.goingButton.setImage(UIImage(named: "goingSelected"), for:.selected)
}
}
and in method goingButtonPressed(cell: TableViewCell) change cell to your object
and just set Bool type true or false
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
cell.goingButton.isSelected = object.isSelected
...
}
You need to store the selected rows in an array of index paths, before that I think you should make few enhancements ... or a lot!!
the cell itself should handle it's button, the controller should just keep track of all cells status.
Add these two properties to your cell
class TableViewCell: UITableViewCell {
var indexPath:IndexPath?
var isSelected : Bool = false {
didSet{
if isSelected {
cell.goingButton.setImage(UIImage(named: "goingSelected"), for: UIControlState.normal)
} else {
cell.goingButton.setImage(UIImage(named: "goingDeselected"), for: UIControlState.normal)
}
}
}
// Outlets
#IBOutlet weak var goingButton: UIButton!
var delegate: GoingButtonDelegate?
#IBAction func goingButtonTapped(_ sender: Any) {
self.isSelected = !self.isSelected
delegate?.goingButtonPressed(cell: self)
}
..
...
}
And store the selected cells in your view controller to keep track of each cell status.
extension ViewController: GoingButtonDelegate {
var selectedCells = NSMutableArray()
func goingButtonPressed(cell: TableViewCell) {
if cell.isSelected {
selectedCells.add(cell.indexPath)
} else {
selectedCells.remove(cell.indexPath)
}
}
}
and in your "cell for row" method just add a small change
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "yourCellIdentifier") as! TableViewCell
cell.indexPath = indexPath
cell.isSelected = selectedCells.contains(indexPath)
..
...
return cell
}
I'm working on a project coded in swift 3 and I have a UIButton inside a UITableViewCell where the image changes once the button is tapped. Though the selected buttons get selected as intended, once the UITableview scrolls the selected images gets disappear since the cells are been reused. As I'm new to programming having troubles of writing the logic. Help would much appreciate the code as bellow.
CellFoRow
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
//Button_Action
addSongButtonIdentifier(cell: cell, indexPath.row)
}
This is where the cell is been created.
func addSongButtonIdentifier(cell: UITableViewCell, _ index: Int) {
let addButton = cell.viewWithTag(TABLE_CELL_TAGS.addButton) as! UIButton
//accessibilityIdentifier is used to identify a particular element which takes an input parameter of a string
//assigning the indexpath button
addButton.accessibilityIdentifier = String (index)
// print("visible Index:",index)
print("Index when scrolling :",addButton.accessibilityIdentifier!)
addButton.setImage(UIImage(named: "correct"), for: UIControlState.selected)
addButton.setImage(UIImage(named: "add_btn"), for: UIControlState.normal)
addButton.isSelected = false
addButton.addTarget(self, action: #selector(AddToPlaylistViewController.tapFunction), for:.touchUpInside)
}
the tap function
func tapFunction(sender: UIButton) {
print("IndexOfRow :",sender.accessibilityIdentifier!)
// if let seporated by a comma defines, if let inside a if let. So if the first fails it wont come to second if let
if let rowIndexString = sender.accessibilityIdentifier, let rowIndex = Int(rowIndexString) {
self.sateOfNewSongArray[rowIndex] = !self.sateOfNewSongArray[rowIndex]//toggle the state when tapped multiple times
}
sender.isSelected = !sender.isSelected //image toggle
print(" Array Data: ", self.sateOfNewSongArray)
selectedSongList.removeAll()
for (index, element) in self.sateOfNewSongArray.enumerated() {
if element{
print("true:", index)
selectedSongList.append(songDetailsArray[index])
print("selectedSongList :",selectedSongList)
}
}
}
You need to maintain the button selection status at the controller level. You need to make changes to the model that you are using to configure your tableview.
I have created a similar scenario. I have used an array selectionStatusArray to maintain the button's selection status.
Example:
1. UIViewController containing UITableView
class ViewController: UIViewController, UITableViewDataSource
{
#IBOutlet weak var tableView: UITableView!
var selectionStatusArray = [false, false, false, false, false] //Array that maintains the button selection status
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return selectionStatusArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TableViewCell
addSongButtonIdentifier(cell: cell, indexPath.row)
return cell
}
func addSongButtonIdentifier(cell: TableViewCell, _ index: Int)
{
cell.addButton.tag = index
cell.addButton.isSelected = selectionStatusArray[index]
cell.tapHandler = {
self.selectionStatusArray[$0] = cell.addButton.isSelected
}
}
}
2. Custom UITableViewCell
class TableViewCell: UITableViewCell
{
#IBOutlet weak var addButton: UIButton!
var tapHandler: ((Int)->())?
#IBAction func tapFunction(_ sender: UIButton)
{
sender.isSelected = !sender.isSelected
tapHandler?(sender.tag)
}
}
You can configure the UITableViewCell according to your requirements.
You have this code addButton.isSelected = false which is causing the problem because I believe you are calling the function addSongButtonIdentifier inside tableView delegate method, you should not set all those property inside tableView delegate.
Instead you should do it initially and only once for each of your cell like either in storyboard itself or by providing a model to cell class.
I am trying to run an action for a button being pressed within a table view cell. The following code is in my table view controller class.
The button has been described as "yes" in an outlet in my class of UITableViewCell called requestsCell.
I am using Parse to save data and would like to update an object when the button is pressed. My objectIds array works fine, the cell.yes.tag also prints the correct number to the logs, however, I cannot get that number into my "connected" function in order to run my query properly.
I need a way to get the indexPath.row of the cell to find the proper objectId.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as requestsCell
// Configure the cell...
cell.name.text = requested[indexPath.row]
imageFiles[indexPath.row].getDataInBackgroundWithBlock{
(imageData: NSData!, error: NSError!) -> Void in
if error == nil {
let image = UIImage(data: imageData)
cell.userImage.image = image
}else{
println("not working")
}
}
cell.yes.tag = indexPath.row
cell.yes.targetForAction("connected", withSender: self)
println(cell.yes.tag)
return cell
}
func connected(sender: UIButton!) {
var query = PFQuery(className:"Contacts")
query.getObjectInBackgroundWithId(objectIDs[sender.tag]) {
(gameScore: PFObject!, error: NSError!) -> Void in
if error != nil {
NSLog("%#", error)
} else {
gameScore["connected"] = "yes"
gameScore.save()
}
}
}
Swift 4 & Swift 5:
You need to add target for that button.
myButton.addTarget(self, action: #selector(connected(sender:)), for: .touchUpInside)
And of course you need to set tag of that button since you are using it.
myButton.tag = indexPath.row
You can achieve this by subclassing UITableViewCell. Use it in interface builder, drop a button on that cell, connect it via outlet and there you go.
To get the tag in the connected function:
#objc func connected(sender: UIButton){
let buttonTag = sender.tag
}
The accepted answer using button.tag as information carrier which button has actually been pressed is solid and widely accepted but rather limited since a tag can only hold Ints.
You can make use of Swift's awesome closure-capabilities to have greater flexibility and cleaner code.
I recommend this article: How to properly do buttons in table view cells using Swift closures by Jure Zove.
Applied to your problem:
Declare a variable that can hold a closure in your tableview cell like
var buttonTappedAction : ((UITableViewCell) -> Void)?
Add an action when the button is pressed that only executes the closure. You did it programmatically with cell.yes.targetForAction("connected", withSender: self) but I would prefer an #IBAction outlet :-)
#IBAction func buttonTap(sender: AnyObject) {
tapAction?(self)
}
Now pass the content of func connected(sender: UIButton!) { ... } as a closure to cell.tapAction = {<closure content here...>}. Please refer to the article for a more precise explanation and please don't forget to break reference cycles when capturing variables from the environment.
Simple and easy way to detect button event and perform some action
class youCell: UITableViewCell
{
var yourobj : (() -> Void)? = nil
//You can pass any kind data also.
//var user: ((String?) -> Void)? = nil
override func awakeFromNib()
{
super.awakeFromNib()
}
#IBAction func btnAction(sender: UIButton)
{
if let btnAction = self.yourobj
{
btnAction()
// user!("pass string")
}
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = youtableview.dequeueReusableCellWithIdentifier(identifier) as? youCell
cell?.selectionStyle = UITableViewCellSelectionStyle.None
cell!. yourobj =
{
//Do whatever you want to do when the button is tapped here
self.view.addSubview(self.someotherView)
}
cell.user = { string in
print(string)
}
return cell
}
We can create a closure for the button and use that in cellForRowAtIndexPath
class ClosureSleeve {
let closure: () -> ()
init(attachTo: AnyObject, closure: #escaping () -> ()) {
self.closure = closure
objc_setAssociatedObject(attachTo, "[\(arc4random())]", self,.OBJC_ASSOCIATION_RETAIN)
}
#objc func invoke() {
closure()
}
}
extension UIControl {
func addAction(for controlEvents: UIControlEvents = .primaryActionTriggered, action: #escaping () -> ()) {
let sleeve = ClosureSleeve(attachTo: self, closure: action)
addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: controlEvents)
}
}
And then in cellForRowAtIndexPath
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = youtableview.dequeueReusableCellWithIdentifier(identifier) as? youCell
cell?.selectionStyle = UITableViewCell.SelectionStyle.none//swift 4 style
button.addAction {
//Do whatever you want to do when the button is tapped here
print("button pressed")
}
return cell
}
class TableViewCell: UITableViewCell {
#IBOutlet weak var oneButton: UIButton!
#IBOutlet weak var twoButton: UIButton!
}
class TableViewController: UITableViewController {
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TableViewCell
cell.oneButton.addTarget(self, action: #selector(TableViewController.oneTapped(_:)), for: .touchUpInside)
cell.twoButton.addTarget(self, action: #selector(TableViewController.twoTapped(_:)), for: .touchUpInside)
return cell
}
func oneTapped(_ sender: Any?) {
print("Tapped")
}
func twoTapped(_ sender: Any?) {
print("Tapped")
}
}
With Swift 5 this is what, worked for me!!
Step 1. Created IBOutlet for UIButton in My CustomCell.swift
class ListProductCell: UITableViewCell {
#IBOutlet weak var productMapButton: UIButton!
//todo
}
Step 2. Added action method in CellForRowAtIndex method and provided method implementation in the same view controller
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ListProductCell") as! ListProductCell
cell.productMapButton.addTarget(self, action: #selector(ListViewController.onClickedMapButton(_:)), for: .touchUpInside)
return cell
}
#objc func onClickedMapButton(_ sender: Any?) {
print("Tapped")
}
As Apple DOC
targetForAction:withSender: Returns the target object that responds to
an action.
You can't use that method to set target for UIButton.
Try
addTarget(_:action:forControlEvents:) method
Get cell like this, Hope it will help someone
#objc func addActionClicked(sender: UIButton) {
let buttonPosition: CGPoint = sender.convert(CGPoint.zero, to: trustedTableView)
let indexPath = trustedTableView.indexPathForRow(at: buttonPosition)
let cell = trustedTableView.cellForRow(at: indexPath!) as! addNetworkSSIDCell
}
in Swift 4
in cellForRowAt indexPath:
cell.prescriptionButton.addTarget(self, action: Selector("onClicked:"), for: .touchUpInside)
function that run after user pressed button:
#objc func onClicked(sender: UIButton){
let tag = sender.tag
}
The accepted answer is good and simple approach but have limitation of information it can hold with tag. As sometime more information needed.
You can create a custom button and add properties as many as you like they will hold info you wanna pass:
class CustomButton: UIButton {
var orderNo = -1
var clientCreatedDate = Date(timeIntervalSince1970: 1)
}
Make button of this type in Storyboard or programmatically:
protocol OrderStatusDelegate: class {
func orderStatusUpdated(orderNo: Int, createdDate: Date)
}
class OrdersCell: UITableViewCell {
#IBOutlet weak var btnBottom: CustomButton!
weak var delegate: OrderStatusDelegate?
}
While configuring the cell add values to these properties:
func configureCell(order: OrderRealm, index: Int) {
btnBottom.orderNo = Int(order.orderNo)
btnBottom.clientCreatedDate = order.clientCreatedDate
}
When tapped access those properties in button's action (within cell's subclass) that can be sent through delegate:
#IBAction func btnBumpTapped(_ sender: Any) {
if let button = sender as? CustomButton {
let orderNo = button.orderNo
let createdDate = button.clientCreatedDate
delegate?.orderStatusUpdated(orderNo: orderNo, createdDate: createdDate)
}
}
Let me offer another approach for getting the cell from a button within it.
The idea is to subclass UIButton only to open a weak pointer to a UITableViewCell.
class MyButton: UIButton {
#objc weak var cell: UITableViewCell?
}
In the UITableViewCell:
override func awakeFromNib() {
super.awakeFromNib()
myButton.cell = self
}
In the ViewController of the table view, where the button is connected:
#IBAction func myButtonAction(sender: MyButton) {
let parentCell = sender.cell
...
}