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 methoddidTapCell
, accepting aUITableViewCell
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 ordidSelectRowAtIndexPath
of your cell, callself.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 itscellDelegate
property toself
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)
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
Open Specific View When Opening App from Notification
Disabled Uibutton Not Faded or Grey
How to Apply a Tintcolor to a Uiimage
Uirefreshcontrol - Pull to Refresh in iOS 7
Export Compliance in iOS App Submission
How to Check If a Framework Is Bitcode Supported for Xcode7
Dynamic Uiimageview Size Within Uitableview
How to Get Selected Value from Uipickerview
How to Debug "Invalid Bundle" Error Which Happens Only After Submitting to App Store
How to Hide/Show Tabbar When Tapped Using Swift in iOS8
Ios: Place Uiview on Top of Uitableview in Fixed Position
Watch Os 2.0 Beta: Access Heart Beat Rate
How to Change the Size of Uiactivityindicator
Uitextview Starts at Bottom or Middle of the Text
How to Generate an iOS IPA File with Ionic Framework