Aovib Aovib - 11 months ago 40
Swift Question

(Swift) How to pass data back and forth between UITableView and UITableViewCell?

I'm a beginning iOS developer and I've been stuck on an issue for quite some time now.

I have a single viewcontroller in which I have placed a TableView (I cannot use a tableviewcontroller). It holds 4 dynamic prototype cells:
Cell 1 has an UITextField and a couple of labels, Cell 2-4 only have a label (with different types of information) and have to be hidden initially.

When an user enters a number (max. 3 digits) in the UITextField of the first cell, the number has to be compared to check if it is a correct/existing number. If this number proves correct, 2 things will have to happen: 1) the labels in the first cell will need to change layout (colour, font, size, ...) and set the data of one label. 2) the other cells in the tableview will have to appear beneath the first cell.

I'm having trouble sending data back and forth between the first UITableViewCell and its UITableView. For the text input I use the shouldChangeCharactersInRange method in the cell class, which then limits the # of digits and calls a method in the tableView class where the data will be compared. For this I used delegation.

However, after checking whether the number is a match, I need to call a method in the cell class (from within the tableview class) that will change the layout and set the data of a label in the cell. Yet I can't seem to figure out a way to make this method call work and access the outlets.

Summarised: cell class sends number to tableview class, methods in tableview class run, tableview class sends bool to cell class where outlets need to be changed.

What I tried:
I tried setting up delegation in the other direction, but it wouldn't trigger. Using a normal method call wouldn't work either, because then the outlets are nil.

I believe my problem lies in the fact that I need to reference the same instance/object of the cell to access the outlets?

I selected and simplified the relevant pieces of code:

1) TableView class

class TableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, updateUITableView {

var existingNumber = 200
var cellsRowHeight = false

@IBOutlet var tableView: UITableView!

//Started by InputTableViewCell when UITextField has a 3 digit-number
func checkNumberInput(inputNumber: Int) {
//Step 1: check if the number matches an existing one
let match = checkNumberMatch(inputNumber: inputNumber)

//Step 2: send a bool back to the cell class to change the layout through outlets
InputTableViewCell().changeLayout(numberMatch: match) // <--- problem

//Step 3: make the hidden cells appear
toggleCellsVisibility(numberMatch: match)

//Step 1
func checkNumberMatch(inputNumber: Int) -> Bool {
if inputNumber == existingNumber {
return true
} else {
return false

//Step 2
func toggleCellsVisibility(numberMatch: Bool) {
cellsRowHeight = numberMatch
if numberMatch == true { //cells appear
//possible additional code
} else { //cells dissappear
//possible additional code

//Step 3
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
switch (indexPath.row) {
case 0 where !cellsRowHeight || cellsRowHeight:
return 170
case 1 where !cellsRowHeight:
return 0
case 1 where cellsRowHeight:
return 54
//cases for other cells to appear/dissappear
return 44

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
//Create tableViewCell
let cellIdentifier = "InputCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! InputTableViewCell

cell.delegate = self

//Customize lay-out of cell
cell.preservesSuperviewLayoutMargins = false
cell.separatorInset =
cell.layoutMargins =

return cell
//creation of other cells

// MARK: - Loading
override func viewDidLoad() {
tableView.tableFooterView = UIView(frame:

2) Cell class:

protocol updateUITableView: class {
func checkNumberInput(inputNumber: Int)

class InputTableViewCell: UITableViewCell, UITextFieldDelegate {
var delegate: updateUITableView?

@IBOutlet var textField: UITextField!
@IBOutlet var letterLabel: UILabel!

//Step 2: problem!
func changeLayout(numberMatch: Bool) {
if numberMatch == true {
print("Success") //this line triggers
letterLabel?.text = "A" //is nil
//other lay-out changes
else {
print("Fail, please provide an existing number")
//other lay-out changes

//Set maximum character limit in textField and dismiss keyboard when character limit is reached.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let currentCharacterCount = textField.text?.characters.count ?? 0
let newLength = currentCharacterCount + string.characters.count - range.length

if (newLength == 3) {
textField.text = (textField.text! as NSString).replacingCharacters(in: range, with: string)

//Get text from textField
let numberInput: Int? = Int(textField.text!)
if numberInput != nil {
delegate?.checkNumberInput(number: numberInput!) //send number to tableview class
//Dismiss keyboard

if (range.length + range.location > currentCharacterCount) {
return false
} else {
return true
return true

func viewDidLoad() {

The actual problem is situated in "Step 2". Using this method I can perform the print statement, but the actual labels/outlets are nil because it is just a generic call.

Any help would be immensely appreciated! Thank you!

Answer Source

Pass your UITableViewCell instance in your delegate method like this

func checkNumberInput(senderCell: InputTableViewCell,inputNumber: Int)

and then you will be able to call any method on this cell's instance. So in your case.

func checkNumberInput(senderCell: InputTableViewCell,inputNumber: Int){
   senderCell.changeLayout(numberMatch: match) 

and you can call your method like

delegate?.checkNumberInput(senderCell: self, inputNumber: numberInput!)