How to Reference Uitableviewcontroller from a Uitableviewcell Class

How to reference UITableViewController from a UITableViewCell class?

I wouldn't create this kind of dependency between the cell and the view controller - that makes the architecture more intricate and the cell not reusable.

I suggest you to use the delegation pattern, which may sound a little complicated - although you're already using (UITableViewDelegate is a typical example):

  • create a protocol MyCellProtocol with one method didTapCell, accepting a UITableViewCell and/or some custom data you want to pass to the view controller
  • create a public delegate property in your custom cell: weak var cellDelegate: MyCellProtocol?
  • in the didTapXXX handler or didSelectRowAtIndexPath of your cell, call self.cellDelegate?.didTapCell(), passing the expected parameters
  • in your view controller, implement the MyCellProtocol
  • in cellForRowAtIndexPath of your view controller, when creating/dequeuing the cell, set its cellDelegate property to self

At this point, when a tap is done in your cell, the didTapCell method of the view controller is called, and from there you can do whatever you need to achieve.

The key point is: rather than making the cell handle the cell tap/selection, notify the view controller and let it do the job.

How to find ViewController for UITableViewCell?

You can try

[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alert animated:true completion:nil];

How to interact with custom UITableViewCell class?

As I understood, you have a TableViewController with customs UITableView cells.
Each cell contains medicine name, its number and the UIStepper (that allows to change the number of that medicine).
You goal is to update medicalCountLabel every time the UIStepper is tapped.

(from the conversation in the comments, this is what I implemented)
Sample Image

Custom UITableViewCell Class

import UIKit

class TableViewCell: UITableViewCell {
//MARK: - Properties
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var countLabel: UILabel!
@IBOutlet weak var stepper: UIStepper!

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
}

//MARK: - Actions

@IBAction func stepperTapped(_ sender: UIStepper) {
countLabel.text = Int(sender.value).description
} //this is where you update `medicalCountLabel`

}

TableViewController Class:

import UIKit

class TableViewController: UITableViewController {
//MARK: - Properties
var medicines = [Medicine(name: "Парацетамол", count: 4), Medicine(name: "Ибуфен", count: 7),Medicine(name: "Цитрамон", count: 4),Medicine(name: "Смекта", count: 9), Medicine(name: "Мезин", count: 2)]

override func viewDidLoad() {
super.viewDidLoad()
}

// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return medicines.count
}


override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? TableViewCell else{
fatalError("The dequeued cell is not an instance of TableViewCell.")
}

// Configure the cell
let medicine = medicines[indexPath.row]
cell.nameLabel.text = medicine.name
cell.countLabel.text = String(medicine.count)
cell.stepper.value = Double(medicine.count)
return cell
}
}

Medicine Class (in your case you just did it using struct which is also fine; but I created a separate swift file):

import UIKit
class Medicine{
var name: String
var count: Int

init(name: String, count: Int){
self.name = name
self.count = count
}
}

Access ViewController from action in TableViewCell

I guess you should do it by creating property of your controller in cell class and assign property value when after creating cell in cellForRowAtIndexPath method.

For example:

in cell class

weak var yourController : YourViewController?

in cellForRowAtIndexPath

cell.yourController = self

then you can access the YourViewController in editBtnPressed action.

But i suggest you to do by creating button action programmatically in your controller class. that's the good approach.

for example:

class YourCellClass: UITableViewCell {
@IBOulet var myBtn: UIbutton!
...
}

Yourcontroller class

in cellForRowAtIndexPath

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

let cell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! YourCellClass

cell.myButton.addTarget(self, action: #selector(self. editBtnPressed), for: .touchUpInside)

and in editBtnPressed

func editBtnPressed (_ sender: Any) {
// you can access controller here by using self

}

Reference from UITableViewCell to parent UITableView?

Store a weak reference to the tableView in the cell, which you'd set in -tableView:cellForRowAtIndexPath: of your table's dataSource.

This is better than relying on self.superview to always be exactly the tableView is fragile. Who knows how Apple might re-organize the view hierarchy of UITableView in the future.

iphone - access uitableviewcontroller from uitableviewcell

I usually maintain a weak reference from my UIView to my UIViewController if I need one, usually by creating a method something like this:

-(MyView*)initWithController:(CardCreatorViewController*) aController andFrame:(CGRect)aFrame
{
if (self = [super initWithFrame:aFrame])
{
controller = aController;
// more initialisation here
}

return self;
}

You could also use a delegate pattern if you want a more decoupled solution. I tend to think this is overkill for a view and its controller, but I would use it with a system of controllers and subcontrollers.

Add a Selector from a UITableViewCell class to the UITableViewController class

In alternative to the accepted solution:

Depending on case you can try use delegate instead action/target

//Declare a delegate for the cell
protocol PlayerSelectionCellDelegate : class {
func playerSelectionCell(_ cell:PlayerSelectionCell, didChangeValue:Double)
func playerSelectionCell(_ cell:PlayerSelectionCell, expansionSelectionValueDidChange:Bool)
}

class PlayerSelectionCell: UITableViewCell {

weak var delegate : PlayerSelectionCellDelegate? //Set a weak var to allocate the delegate

@objc func stepperValueDidChange(_ sender: UIStepper) {
delegate?.playerSelectionCell(self, didChangeValue: sender.value)
}

@objc func expansionSelectionValueDidChange(_ sender: UISwitch) {
delegate?.playerSelectionCell(self, expansionSelectionValueDidChange: sender.isOn)
}

override func awakeFromNib() {
let stepper = UIStepper()
stepper.addTarget(self, action: #selector(stepperValueDidChange), for: .valueChanged)

//This line should be reference to the UISwitch Action
selectionSwitch.addTarget(self, action: #selector(expansionSelectionValueDidChange), for: .valueChanged)
self.accessoryView = stepper
}
}

In the ViewController you change the code to get in conformity to the delegate and set the ViewController as delegated to cell

class SetupViewController: UITableViewController {

@IBOutlet var setupTableView: UITableView!

override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}

override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch section {
case 0:
return "Expansions"
case 1:
return "Players"
default:
return nil
}
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0:
return Expansions.allCases.count
case 1:
return 3
default:
return 0
}
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "Expansion Selection", for: indexPath) as? ExpansionCell ?? ExpansionCell()
cell.textLabel?.text = Expansions.allCases[indexPath.row].rawValue
return cell
} else if indexPath.section == 1 && indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "Player Selection", for: indexPath) as? PlayerSelectionCell ?? PlayerSelectionCell()
cell.delegate = self //Set the ViewController to be delegated by cell
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "Player Detail", for: indexPath) as? PlayerCell ?? PlayerCell()
cell.textLabel?.text = "Player \(indexPath.row)"
return cell
}
}

override func viewDidLoad() {
super.viewDidLoad()
}

//Remove the actions and implements the delegate methods
func playerSelectionCell(_ cell: PlayerSelectionCell, didChangeValue players: Double) {
print("Value Players: \(players)")
}

func playerSelectionCell(_ cell: PlayerSelectionCell, expansionSelectionValueDidChange selected: Bool) {
print("Value Selected: \(selected)")
}

}


Related Topics



Leave a reply



Submit