Button Action from UITableViewCell to ViewController - ios

I am trying to implement the Delegate way to do some action on a button tap from my UITableViewCell to a ViewController. The following is what I have so far -
TableViewCell:
protocol UserCellDelegate : class {
func disableUser(cell: UserCell, button: UIButton)
}
class UserCell : UITableViewCell {
#IBOutlet weak var userLabel: UILabel!
#IBOutlet weak var userDescription: UILabel!
#IBOutlet weak var writeOutUser: UIButton!
weak var delegate: UserCellDelegate?
override func awakeFromNib() {
super.awakeFromNib()
writeOutUser.layer.cornerRadius = 5.0
}
#IBAction func disableUserButtonAction(sender: UIButton) {
delegate?.disableUser(self, button: writeOutUser) //self here I believe to be the cell
}
}
ViewController:
class UserDetailTableViewController : UIViewController, UserCellDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let userCell = UserCell()
userCell.delegate = self
}
func disableUser(cell: UserCell, button: UIButton) {
print("VC: User Disabled")
}
}
The problem here is, the disableUser function in my ViewController never gets called. What am I doing wrong?
What is the best way to do it?
I had referred to the SO Approved Answer Here which is what I am following the same, but with no luck.
Any help will be appreciated. Thanks!!

You should set the delegate of your cell in the cellForRowAtIndexPath method.As #vadian said,In the viewDidLoad you should not initialize your cell using default initializer, and the delegate is nil for your cell by this way in viewDidLoad.
Either initialise your cell using the init(style: reuseIdentifier) or set the delegate in the cell for row method as you will initialize the cell in that method anyway.

When you're doing this:
let userCell = UserCell()
userCell.delegate = self
You are setting the delegate of userCell, and only the delegate of userCell. I'm sure that the table view cells displayed in your table view is not userCell.
So instead of setting the delegate of userCell, you should set the delegate of the cells that you actually want to show in the table view. The most probable place to create new cells that you want to show in table view is in the tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) method.
In the method, you must have something like this:
let cell = ...
// setting properties of cell using the model
return cell
You just need to add this line before the return:
cell.delegate = self

In your code UserCell acts like a UIView. You forgot adding userCell to current controller's view. If you want to use UITableViewCell, the better way is using it with UITableView.

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)

How can I call viewDidLoad in a UITableViewCell?

My code:
import Foundation
import Firebase
class CellOneViewController: UITableViewCell {
#IBOutlet weak var new1: UILabel!
let ref = Firebase(url: "https://burning-heat-8250.firebaseio.com/slide2")
func viewdidload() {
ref.observeEventType (.Value, withBlock: { snapshot in
self.new1.text = snapshot.value as? String
})
}
}
I've read around that you can't call viewDidLoad in a UITableViewCell, only in a UITableViewController. All the answers are in Objective-C, but I'm writing the app in Swift. I don't receive any critical errors but when running the app nothing appears in the cell where the label is. I'm fairly new to using Xcode, as I am just going around following guides so if I'm saying something incorrect let me know.
I think, you need method func layoutSubviews().
Only ViewController gets func viewDidLoad() called, after view is loaded.
If you need to initialize something or update views, you need to do in layoutSubviews(). As soon, your view or UITableViewCell gets loaded, layoutSubviews() get called.
Replace viewDidLoad with layoutSubviews()
class CellOneViewController: UITableViewCell {
#IBOutlet weak var new1: UILabel!
let ref = Firebase(url: "https://burning-heat-8250.firebaseio.com/slide2")
override func layoutSubviews() {
super.layoutSubviews()
ref.observeEventType (.Value, withBlock: { snapshot in
self.new1.text = snapshot.value as? String
})
}
}
The reason you can't do this is that a UITableViewCell isn't a subclass of UIViewController.
The cellForRowAtIndexPath method in the UITableViewDataSource is where you should set up your cells. You probably only want to do something like this in your custom UITableViewCell:
class CellOne: UITableViewCell {
#IBOutlet weak var new1: UILabel!
}
Then in your TableViewController's cellForRowAtIndexPath method (provided you have imported firebase) you should dequeue a reusable cell, cast it as a CellOne (as! cellOne) and then you can set the new1.text value. I don't know what your reuse identifier is, but it would look something like this:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Your-Reuse-Identifier", forIndexPath: indexPath) as! CellOne
cell.new1.text = "Your Value"
return cell
}

Swift custom UITableViewCell label is always nil

I've been stuck with this problem for days, so I'd be really happy if someone could help.
I'm trying to create a dynamic UITableView, for which I created a custom UITableView subclass and I've created a custom UITableViewCell subclass as well, because I need several UILabels and a UIButton in each cell.
The cell is created, but the problem is that the value of the labels is always nil, hence the cell isn't displayed properly.
This is, how the storyboard looks like, and this is what I see while running the program.
Here's my UITableViewCell subclass:
import UIKit
class QuestionTableViewCell: UITableViewCell {
#IBOutlet var student: UILabel!
#IBOutlet var labDesk: UILabel!
#IBOutlet var topic: UILabel!
#IBOutlet var answers: UILabel!
}
and my UITableView subclass:
import UIKit
class QuestionViewController: UITableViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var table: UITableView!
struct Question {
var student: String
var labDesk: String
var topic: String
var answered: String
}
override func viewDidLoad() {
super.viewDidLoad()
table.estimatedRowHeight = 50
table.dataSource = self
table.delegate = self
self.table.registerClass(QuestionTableViewCell.self, forCellReuseIdentifier: "cell")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as QuestionTableViewCell
cell.student.text = "random string"
cell.labDesk?.text = "25/A"
cell.topic?.text = "string"
cell.answers?.text = "3"
return cell
}
}
Try removing self.table.registerClass(QuestionTableViewCell.self, forCellReuseIdentifier: "cell")
If you're using a cell with a nib then make sure that you are registering the cell with the table view using registerNib:forCellReuseIdentifier:. If the cell just has a class then use registerClass:forCellReuseIdentifier:.
First, you don't have to register the class if it exists in Interface Builder.
Second, you should dequeueReusableCellWithIdentifier:forIndexPath instead of dequeueReusableCellWithIdentifier.
Third, UITableViewController already has a property called tableView so there is no need to make an IBOutlet to table as UITableViewController already handles this. It also conforms to the UITableViewDataSource and UITableViewDataSource so these are extraneous.
Fourth, don't set the properties for table set them for tableView.
Fifth, cell.labDesk.text = "" is sufficient, no need to make it optional.
If all your IBOutlets are hooked up, Cell Identifiers correctly set, and these revisions are made, it will work.
class QuestionTableViewCell: UITableViewCell {
#IBOutlet var student: UILabel!
#IBOutlet var labDesk: UILabel!
#IBOutlet var topic: UILabel!
#IBOutlet var answers: UILabel!
}
class QuestionViewController: UITableViewController {
struct Question {
var student: String
var labDesk: String
var topic: String
var answered: String
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.estimatedRowHeight = 50
tableView.dataSource = self
tableView.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as QuestionTableViewCell
cell.student.text = "random string"
cell.labDesk.text = "25/A"
cell.topic.text = "string"
cell.answers.text = "3"
return cell
}
}
The most important part is to register the xib containing the custom cell with the table view. Therefore add the following code in viewDidLoad() method.
let nib = UINib.init(nibName: "MyCustomCell", bundle: nil)
self.tblUsers.register(nib, forCellReuseIdentifier: "MyCustomCell")
I might be late here, but I just solved a similar problem.
Make sure you've set the Identifier in InterfaceBuilder on your UITableViewCell.
For those who are still trying to figure this out after trying all those possible solutions:
Disconnect/Reconnect the IBOutlets in your Storyboards should do the trick!
Don't forget to add:
tableView?.register(UINib(nibName: "xyz",
bundle: nil),
forCellReuseIdentifier: "abc")
If you are using a table cell with Xib. you need to register your cell with ..
register(_:forCellReuseIdentifier:)
If you haven't added constraints for the label then they will not be created though the custom cell is created.
Make sure you added some constraints.
Make sure that the selected cell is in the right "module" and if necessary, inherit:
If not, your IBOutlets will be nil.
Issue I was facing: TableViewCell has been created and all the IBOutlets are nil. So I can't set any values such as text or color etc. Below code worked for me.
Xcode version: 13.3
Step 1:
Remove datasource and delegate reference form storyboard.
Step 2:
In viewDidLoad add,
tableView.delegate = self
tableView.dataSource = self
Step 3:
In tableview UITableViewDataSource cellForRowAt function, add your cell the given way.
let cell = tableView.dequeueCell(ofType: YourCellName.self)
cell.yourCellFunction()
return cell
Note 1: dequeueCell(ofType...) is calling the below function internally. you don't need to use it directly.
func dequeueCell<T: UITableViewCell>(ofType type: T.Type) -> T {
}
Important: You don't need to provide any "Resporation ID" or "Reuse Identifier" for cell. It works with your cell name.

IBAction on UISwitch in custom UITableViewCell causes error

I have a UITableViewController and a custom TableViewCell and within that UITableViewCell there is a UISwitch. This switch is wired to an IBAction, but as soon as i tap the switch, i get an error:
unrecognised selector sent to instance 0x13ce30a50
SelectFriendsViewController.swift
class SelectFriendsViewController: UITableViewController, SelectorDelegate {
func selectUser(string: String) {
selectedUser = string;
}
.... lots of code removed for simplification.
override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:SelectorTableViewCell = tableView!.dequeueReusableCellWithIdentifier("MyCell", forIndexPath: indexPath) as SelectorTableViewCell;
cell.delegate = self;
}
}
protocol SelectorDelegate {
func selectUser(string: String)
}
class SelectorTableViewCell: UITableViewCell {
#IBOutlet var swUser: UISwitch! = UISwitch();
#IBOutlet var lblUserName: UILabel! = UILabel();
var delegate: SelectorDelegate!
#IBAction func SwitchUser(sender: UISwitch) {
//delegate.selectUser("test");
//even with just this println i get the error
println("test");
}
}
You have some strange stuff in this code:
your cellForRowAtIndexPath should return a cell (you probably have done this, and just didn't copy it across to your stackoverflow question)
You're generating your UISwitch either from storyboard or xib as you say 'the switch on the storyboard is highlighted' - however you're also instantiating these in code!
eg.
#IBOutlet var swUser: UISwitch! = UISwitch();
But I believe your problem in the end is related to your IBAction 'SwitchUser'. You either renamed this method at some point or created an IBAction earlier and then deleted it. To check the current status of your IBActions, click on your cell in the storyboard or xib and open the Connections Inspector. I bet you'll find your problem there.
Try changing the function name to switchUser instead of SwitchUser
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html#//apple_ref/doc/uid/TP40014097-CH10-XID_243

Get the IndexPath from inside a UITableViewCell subclass in Swift

I have a custom UITableViewCell subclass and its associated xib. I have a UILabel and a UIButton in this cell and I have wired the touch up inside action of the button to the subclass.
What I need is when that button in the cell is tapped, to get the indexpath of the cell which has that button. And maybe send it back to the view controller via a delegate or something.
Since I'm inside a UITableViewCell subclass, I can't use a solution like this because I don't have a reference to the tableview from inside the cell subclass. Upon further investigation I found another solution and I implemented it in Swift like this.
import UIKit
class ContactCell: UITableViewCell {
#IBOutlet weak var nameLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
selectionStyle = .None
}
#IBAction func callButtonPressed(sender: UIButton) {
let indexPath = (self.superview as UITableView).indexPathForCell(self)
println("indexPath?.row")
}
}
But when I tap on the button, it crashes with an error message saying Swift dynamic cast failed.
Any idea what's wrong with my code?
Or I'm open to any other suggestions which would allow me to achieve the desired result in any other way.
Thank you.
Sounds like you need a delegate:
Delegates in swift?
Then just pass the cell itself as a parameter to the delegate, and then you can easily do tableView.indexPathForCell(cellFromDelegateMethod)
Hey you can use "Tag" of the button also.Inside the cellForRowAt method of table delegate u can tag the button with Indexpath.row . here is the example what i m tried to say.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// get ur cell nib .As it has a button
cell.startOrConntinuBtn.addTarget(self, action: #selector(sumbitOrContinue), for: .touchUpInside)
cell.startOrConntinuBtn.tag = indexPath.row }
and in the touch method "sumbitOrContinue" -
func sumbitOrContinue(sender: UIButton!) {
let tag = sender.tag
// do what you want to do like this example
let detail = self.detailList[tag]
let vc = self.storyboard?.instantiateViewController(withIdentifier: "mockExamInt") as! MockWindowVc
vc.detailId = detail.id
self.navigationController?.pushViewController(vc, animated: true)}
UIButton.Type really does not have member superview, but sender have
var cell: UITableViewCell = sender.superview.superview as UITableViewCell

Resources