UIButton action in table view cell - ios

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
...
}

Related

Change button title in section of tableview on specific row

I have view controller in which there are multiple section of tableview. In section 0 I have multiple row . Each row having button named as Add Comments when I click on button it pushes me to other view controller having text field when i wrote something and press done button then through delegate I passes textfield data and set it in button title. But problem is my button present in all row changes value. I want only selected row in section changes its button title. below is my code of first viewcontroller
class MyTabViewController: UIViewController {
var addCommentsValueStore: String = "Add Comments"
#IBOutlet weak var tabTableView : ContentWrappingTableView!
#IBAction func addCommentsAction(_ sender: UIButton) {
guard let nextVC = MyCommentsRouter.getMyCommentsViewScreen() else { return }
nextVC.passAddCommentsDelegate = self
self.navigationController?.pushViewController(nextVC, animated: true)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if(indexPath.section == 0)
{
let indetifier = "MyTabTableViewCell"
let cell = tableView.dequeueReusableCell(withIdentifier: indetifier, for: indexPath) as! MyTabTableViewCell
cell.addCommentsButton.setTitle(addCommentsValueStore, for: UIControl.State.normal)
}
return cell
}
extension MyTabViewController: AddCommentsDelegate{
func passAddComments(instruction: String) {
addCommentsValueStore = instruction
print(addCommentsValueStore)
}
}
}
below is code of second view controller:
import UIKit
protocol AddCommentsDelegate{
func passAddComments(instruction: String)
}
class MyCommentsViewController: UIViewController {
#IBOutlet var addCommentsTextField: UITextField!
var passAddCommentsDelegate: AddCommentsDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func backActionClick(_ sender: UIButton) {
self.navigationController?.popViewController(animated: true)
}
#IBAction func DoneActionClick(_ sender: Any) {
let dataToBeSent = addCommentsTextField.text
self.passAddCommentsDelegate?.passAddComments(instruction: dataToBeSent!)
self.navigationController?.popViewController(animated: true)
}
}
by using tag on the cell button, you can fix your problem.
on your cellForRowAt delegate method, put this line:
cell.addCommentsButton.tag = indexPath.item
now you know exactly which button did select, then you can use this tag to specify which row in your tableView should change its title.
your implementation has some problems:
first of all, the addCommentsValueStore have to be an array of strings.
var addCommentsValueStore: [String] = []
then tell the delegate to show the right title:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell: MyTabTableViewCell!
if (indexPath.section == 0) {
cell = tableView.dequeueReusableCell(withIdentifier: "MyTabTableViewCell", for: indexPath) as! MyTabTableViewCell
cell.tag = indexPath.item
cell.addCommentsButton.setTitle(addCommentsValueStore[indexPath.item], for: UIControl.State.normal)
}
return cell
}
the AddCommentsDelegate should return the index too:
protocol AddCommentsDelegate {
func passAddComments(instruction: String, atIndex: Int)
}
then every time you want to pass comment to another viewController, you should pass the index too.
by using this index, you will specify where you should change the row button title.
#UIBotton func DoneActionClick(_ sender: UIButton) {
let dataToBeSent = addCommentsTextField[sender.tag].text
self.passAddCommentsDelegate?.passAddComments(instruction: dataToBeSent!, atIndexPath: sender.tag)
self.navigationController?.popViewController(animated: true)
}

add a button to a custom cell that displays another modal view controller [duplicate]

In my main page, I created a xib file for UITableViewCell. I'm loading the cell from that xib file and its working fine.
Inside of the cell I have some labels and buttons. I'm aiming to change the label by clicking to the button on the cell.
My Code likes below
import UIKit
class SepetCell: UITableViewCell{
#IBOutlet var barcode: UILabel!
#IBOutlet var name: UILabel!
#IBOutlet var fav: UIButton!
#IBOutlet var strep: UIStepper!
#IBOutlet var times: UILabel!
#IBAction func favoriteClicked(sender: UIButton) {
println(sender.tag)
println(times.text)
SepetViewController().favorite(sender.tag)
}
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
}
}
This is my xib files behind codes as .swift.
The codes in the main page likes below:
import UIKit
import CoreData
class SepetViewController: UIViewController, UITextFieldDelegate, UITableViewDataSource, UITableViewDelegate {
#
IBOutlet
var sepetTable: UITableView!
var barcodes: [CART] = []
let managedObjectContext = (UIApplication.sharedApplication().delegate as!AppDelegate).managedObjectContext
override func viewWillAppear(animated: Bool) {
if let moc = self.managedObjectContext {
var nib = UINib(nibName: "SepetTableCell", bundle: nil)
self.sepetTable.registerNib(nib, forCellReuseIdentifier: "productCell")
}
fetchLog()
sepetTable.reloadData()
}
func fetchLog() {
if let moc = self.managedObjectContext {
barcodes = CART.getElements(moc);
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) - > Int {
return self.barcodes.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) - > UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("productCell") as ? SepetCell
if cell == nil {
println("cell nil")
}
let product: CART
product = barcodes[indexPath.row]
cell!.barcode ? .text = product.barcode
cell!.name ? .text = product.name
cell!.fav.tag = indexPath.row
return cell!
}
func favorite(tag: Int) {
}
}
When i clicked fav button inside of the Cell. I wanted to change times label text to anything for example.
When I clicked to the fav button, the event will gone to the SepetCell.swift favoriteClicked(sender: UIButton) function.
So if i try to call:
SepetViewController().favorite(sender.tag)
It will go inside of the
func favorite(tag: Int) {
sepetTable.reloadData()
}
but sepetTable is nil when it is gone there. I think it is because of when I call this SepetViewController().favorite(sender.tag) function. It firstly creates SepetViewController class. So because of object is not setted it is getting null.
How can I reach that sepetTable or what is the best way to solve this issue.
Thanks.
Popular patterns for solving this problem are closures and delegates.
If you want to use closures, you would do something like this:
final class MyCell: UITableViewCell {
var actionBlock: (() -> Void)? = nil
then
#IBAction func didTapButton(sender: UIButton) {
actionBlock?()
}
then in your tableview delegate:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) - > UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MyCellIdentifier") as? MyCell
cell?.actionBlock = {
//Do whatever you want to do when the button is tapped here
}
A popular alternative is to use the delegate pattern:
protocol MyCellDelegate: class {
func didTapButtonInCell(_ cell: MyCell)
}
final class MyCell: UITableViewCell {
weak var delegate: MyCellDelegate?
then
#IBAction func didTapButton(sender: UIButton) {
delegate?.didTapButtonInCell(self)
}
..
Now in your view controller:
then in your tableview delegate:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) - > UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MyCellIdentifier") as? MyCell
cell?.delegate = self
And add conformance to the protocol like this:
extension MyViewController: MyCellDelegate {
didTapButtonInCell(_ cell: MyCell) {
//Do whatever you want to do when the button is tapped here
}
}
Hope this helps!
All patterns above are fine.
my two cents, in case You add by code (for example multiple different cells and so on..)
there is a FAR simple solution.
As buttons allow to specify a "target" You can pass directly the controller AND action to cell/button when setting it.
In controller:
let selector = #selector(self.myBtnAction)
setupCellWith(target: self, selector: selector)
...
in custom cell with button:
final func setupCellWith(target: Any? selector: Selector){
btn.addTarget(target,
action: selector,
for: .touchUpInside)
}
Add target for that button.
button.addTarget(self, action: #selector(connected(sender:)), for: .touchUpInside)
Set tag of that button since you are using it.
button.tag = indexPath.row
Achieve this by subclassing UITableViewCell. button on that cell, connect it via outlet.
To get the tag in the connected function:
#objc func connected(sender: UIButton){
let buttonTag = sender.tag
}
2 am answer: You're over thinking this. Create a custom TableViewCell class; set the prototype cell class to your new custom class; and then create an IBAction.

Having multiple buttons in custom UITableViewCell

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*

Gather textfield text from a tableview cell (Swift)

I have a tableview with one textfield in each cell. I added a target like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customLevelCell") as! LevelTableViewCell
cell.cellTextField.addTarget(self, action: #selector(ViewController.TextfieldEditAction), for: .editingDidEnd)
return cell
}
But found out that I'm not able to use the indexpath.row / sender.tag to get the specific textfield text
#objc func TextfieldEditAction(sender: UIButton) {
}
So my question is how can I get the text after the user has edited one of the textfields.
Also how can i get the indexpath.row or sender.tag which will be used to collect the text they added to that specific textfield.
The easiest way to handle this is probably to use a delegate protocol…
In your cell
protocol LevelTableViewCellDelegate: class {
func levelTableViewCell(_ levelTableViewCell: LevelTableViewCell, didEndEditingWithText: String?)
}
class LevelTableViewCell: UITableViewCell {
#IBOutlet private weak var cellTextField: UITextField!
var delegate: LevelTableViewCellDelegate?
override func awakeFromNib() {
cellTextField.addTarget(self, action: #selector(didEndEditing(_:)), for: .editingDidEnd)
}
#objc func didEndEditing(_ sender: UITextField) {
delegate?.levelTableViewCell(self, didEndEditingWithText: sender.text)
}
}
In your view controller
class TableViewController: UITableViewController {
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "LevelTableViewCell") as! LevelTableViewCell
cell.delegate = self
return cell
}
}
extension TableViewController: LevelTableViewCellDelegate {
func levelTableViewCell(_ levelTableViewCell: LevelTableViewCell, didEndEditingWithText: String?) {
let indexPath = tableView.indexPath(for: levelTableViewCell)
// Now you have the cell, indexPath AND the string
}
Also, note that the view outlet is be private. You'll find that you write cleaner code if you follow this rule
Following is the extension of UIView that can be used to get the cell or indexPath of the cell enclosing textField
extension UIView {
var tableViewCell : UITableViewCell? {
var subviewClass = self
while !(subviewClass is UITableViewCell){
guard let view = subviewClass.superview else { return nil }
subviewClass = view
}
return subviewClass as? UITableViewCell
}
func tableViewIndexPath(_ tableView: UITableView) -> IndexPath? {
if let cell = self.tableViewCell {
return tableView.indexPath(for: cell)
}
return nil
}
}
Example :-
#objc func TextfieldEditAction(sender: UITextField) {
//replace tableView with the name of your tableView
guard let indexPath = sender.tableViewIndexPath(tableView) else {return}
}

How to add a button with click event on UITableViewCell in Swift?

In my main page, I created a xib file for UITableViewCell. I'm loading the cell from that xib file and its working fine.
Inside of the cell I have some labels and buttons. I'm aiming to change the label by clicking to the button on the cell.
My Code likes below
import UIKit
class SepetCell: UITableViewCell{
#IBOutlet var barcode: UILabel!
#IBOutlet var name: UILabel!
#IBOutlet var fav: UIButton!
#IBOutlet var strep: UIStepper!
#IBOutlet var times: UILabel!
#IBAction func favoriteClicked(sender: UIButton) {
println(sender.tag)
println(times.text)
SepetViewController().favorite(sender.tag)
}
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
}
}
This is my xib files behind codes as .swift.
The codes in the main page likes below:
import UIKit
import CoreData
class SepetViewController: UIViewController, UITextFieldDelegate, UITableViewDataSource, UITableViewDelegate {
#
IBOutlet
var sepetTable: UITableView!
var barcodes: [CART] = []
let managedObjectContext = (UIApplication.sharedApplication().delegate as!AppDelegate).managedObjectContext
override func viewWillAppear(animated: Bool) {
if let moc = self.managedObjectContext {
var nib = UINib(nibName: "SepetTableCell", bundle: nil)
self.sepetTable.registerNib(nib, forCellReuseIdentifier: "productCell")
}
fetchLog()
sepetTable.reloadData()
}
func fetchLog() {
if let moc = self.managedObjectContext {
barcodes = CART.getElements(moc);
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) - > Int {
return self.barcodes.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) - > UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("productCell") as ? SepetCell
if cell == nil {
println("cell nil")
}
let product: CART
product = barcodes[indexPath.row]
cell!.barcode ? .text = product.barcode
cell!.name ? .text = product.name
cell!.fav.tag = indexPath.row
return cell!
}
func favorite(tag: Int) {
}
}
When i clicked fav button inside of the Cell. I wanted to change times label text to anything for example.
When I clicked to the fav button, the event will gone to the SepetCell.swift favoriteClicked(sender: UIButton) function.
So if i try to call:
SepetViewController().favorite(sender.tag)
It will go inside of the
func favorite(tag: Int) {
sepetTable.reloadData()
}
but sepetTable is nil when it is gone there. I think it is because of when I call this SepetViewController().favorite(sender.tag) function. It firstly creates SepetViewController class. So because of object is not setted it is getting null.
How can I reach that sepetTable or what is the best way to solve this issue.
Thanks.
Popular patterns for solving this problem are closures and delegates.
If you want to use closures, you would do something like this:
final class MyCell: UITableViewCell {
var actionBlock: (() -> Void)? = nil
then
#IBAction func didTapButton(sender: UIButton) {
actionBlock?()
}
then in your tableview delegate:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) - > UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MyCellIdentifier") as? MyCell
cell?.actionBlock = {
//Do whatever you want to do when the button is tapped here
}
A popular alternative is to use the delegate pattern:
protocol MyCellDelegate: class {
func didTapButtonInCell(_ cell: MyCell)
}
final class MyCell: UITableViewCell {
weak var delegate: MyCellDelegate?
then
#IBAction func didTapButton(sender: UIButton) {
delegate?.didTapButtonInCell(self)
}
..
Now in your view controller:
then in your tableview delegate:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) - > UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MyCellIdentifier") as? MyCell
cell?.delegate = self
And add conformance to the protocol like this:
extension MyViewController: MyCellDelegate {
didTapButtonInCell(_ cell: MyCell) {
//Do whatever you want to do when the button is tapped here
}
}
Hope this helps!
All patterns above are fine.
my two cents, in case You add by code (for example multiple different cells and so on..)
there is a FAR simple solution.
As buttons allow to specify a "target" You can pass directly the controller AND action to cell/button when setting it.
In controller:
let selector = #selector(self.myBtnAction)
setupCellWith(target: self, selector: selector)
...
in custom cell with button:
final func setupCellWith(target: Any? selector: Selector){
btn.addTarget(target,
action: selector,
for: .touchUpInside)
}
Add target for that button.
button.addTarget(self, action: #selector(connected(sender:)), for: .touchUpInside)
Set tag of that button since you are using it.
button.tag = indexPath.row
Achieve this by subclassing UITableViewCell. button on that cell, connect it via outlet.
To get the tag in the connected function:
#objc func connected(sender: UIButton){
let buttonTag = sender.tag
}
2 am answer: You're over thinking this. Create a custom TableViewCell class; set the prototype cell class to your new custom class; and then create an IBAction.

Resources