Reputation: 2662
I have a UIView inside a TableViewCell,when users tapped in this UIView,it go to another view controller and pass a data along.
So inside TableViewCellClass I done the following :
I set up a protocol and detected the "Tap" gesture,when user table the UIView.This part is working fine:
protocol MyDelegate : class {
func runThisFunction(myString : String)
}
class MyTableViewCell: UITableViewCell {
weak var delegate : MyDelegate?
...other code here
//here detect the tap
let tap = UITapGestureRecognizer(target: self, action: #selector(hereTapped))
self.myUIElementInThisCell.addGestureRecognizer(tap)
}
@objc func hereTapped(sender: UITapGestureRecognizer? = nil){
self.delegate?.runThisFunction(myString: "I am a man")
}
So in my view controller which contain this TableView,I done the following :
I extend out the MyDelegate
as subclass,and then attach the protocol function inside it as below
class MyViewController: UIViewController,MyDelagate{
func runThisFunction(myString : String) {
print("Tapped in view controller")
self.performSegue(withIdentifier: "MySegue",sender : self)
}
override func viewDidLoad() {
super.viewDidLoad()
let tableViewCell = MyTableViewCell()
tableViewCell.delegate = self
}
}
Result:
After done all the stuff above,when I tapped the UIView
,it didnt perform the segue as stated in MyViewControllerClass
,even the print()
command also didnt execute.
So what I missing out? Please give me a solution.Thanks
Upvotes: 0
Views: 186
Reputation: 100503
The problem is that these 2 lines:
let tableViewCell = MyTableViewCell()
tableViewCell.delegate = self
are not related to the shown cells in the table , it's a cell created on the fly so
set delegate in cellForRow
for the cell that you will actually waiting a delegate trigger from them
cell.delegate = self
like this
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = areaSettTable.dequeueReusableCell(withIdentifier:cellID) as! MyTableViewCell
cell.delegate = self
}
Upvotes: 1
Reputation: 19737
Problem is in your viewDidLoad
:
// you create a new cell
let tableViewCell = MyTableViewCell()
// set its delegate
tableViewCell.delegate = self
// and then the cell is not used for anything else
Basically you are not setting the delegate for the cells that are being presented, but for another instance that you create in viewDidLoad
.
You have to set a delegate in cellForRowAt
to make sure the proper cells get the delegate
set:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "yourIdentifier", for: indexPath) as! MyTableViewCell
cell.delegate = self
return cell
}
This will set the delegate
for those cells, that are presented.
Alternatively, I would recommend using didSelectRowAt
from UITableViewDelegate
(if your MyViewController
implements it):
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0 { // only if it is the first cell (I assume that MyTableViewCell is first)
runThisFunction(myString: "I am a man")
}
}
Upvotes: 0
Reputation: 1261
Delegation in not very good here, indeed if you want to change object that didn't passed to the cell explictitly.
My advice is to use closure:
typealias CellTapHandler = (String)->()
class CustomCell: UITableViewCell {
var handler: CellTapHandler?
@objc func hereTapped(sender: UITapGestureRecognizer? = nil) {
handler?("String or whatever you want to get back from cell.")
}
//...
}
and set up it from view controller
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "yourIdentifier", for: indexPath)
(cell as? MyTableViewCell).handler = { [weak self] string in }
return cell
}
PS: As I said, usually you want to pass object related to cell further. It's difficult to do with delegation, as you had to pass to cell some additional token, to determine object by the cell, o pass object itself breaking Model-View separation paradigm. But it can be done easily with closures:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let object = self.myData[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "yourIdentifier", for: indexPath)
(cell as? MyTableViewCell).handler = { [weak self] string in
self.performSegue(withIdentifier: "MySegue",sender : object)
}
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (let myObj = sender as? MyObjType, let subsequentVC = segue.destination as? NextViewController) {
subsequentVC.selectedMyObject = myObj
}
}
Upvotes: 0
Reputation: 1248
The problem is that the delegate for MyTableViewCell
instances is not defined.
When you do:
override func viewDidLoad() {
super.viewDidLoad()
let tableViewCell = MyTableViewCell()
tableViewCell.delegate = self
}
You are setting a delegate for an object that will be destroyed just when the method viewDidLoad()
finishes.
Solution 1
In order to avoid this, you have to set the delegate inside the cellForRow
method.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "identifier", for: indexPath) as! MyTableViewCell
cell.delegate = self
// Other configurations
return cell
}
Solution 2
You can also use the UITableViewDelegate
methods in order to capture the user interaction with the cells.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
self.performSegue(withIdentifier: "MySegue",sender : self)
}
This way you avoid all the MyDelegate protocol
thing. This would be my preferred option.
Upvotes: 1