Skip to content

How Do You Get the Data From Your Custom UITableViewCells?

You don’t need to progress very far into iOS development before you need to deal with tables. This means: UITableView, and probably your own custom UITableViewCells. A common pattern is to have editable controls in those cells, so that the user can change data from the table itself. Now you’re faced with a problem:

How do you transfer that data back to your UIViewController so the app can use it?

You know how to do this with standard controls, like UITextFields, UIButtons, etc. But you have a UITableView with a bunch of UITableViewCells. They don’t really behave the same way:

  • They’re dynamically constructed by your UITableView
  • There are a variable number of them (usually)
  • Getting access to them from the UITableView is not easy
  • You don’t treat them the same way as “normal” UIViews, such as hooking them up using outlets in Interface Builder

But tear all this away, and your UITableViewCells are not special snowflakes, they are UIViews just like any other. You should treat them exactly that way. Then the answer becomes simple: you do the same thing that Apple does in their UIViews: use delegates. Here’s how.


To set the stage, let’s say you’re writing a multiple choice testing app, where you have a UITableView containing UITableViewCells, each containing 3 buttons to indicate the answers a user could tap. You need to receive that information in order to be able to determine whether the answers are right once the user is complete.

First, define your delegate protocol. How you define it is a matter of personal preference. In this case, we probably don’t need the delegate to understand how the UI for the AnswerCellView is structured, so let’s just send indexes:

protocol AnswerCellViewDelegate {
	func answerCellTapped(answerCellIndex: Int, answerSelected: Int)
}

Now add your delegate to your custom UITableViewCell class:

class AnswerCellView: UITableViewCell {
	// ...

    weak var delegate: AnswerCellViewDelegate?

    // ...
}

Make sure you define your delegate variable as weak. Often, the delegate that gets passed in is owned by an outer UIView or the UIViewController. If you don’t define the delegate as weak in the UITableViewCell, you could cause a retain cycle and a memory leak.

Next, you will need to translate the button taps into calls through the delegate to communicate the state changes (in the case of UIButons, you would have registered these methods using UIButton.addTarget when you built the AnswerViewCell, or hooked them up to outlets if your cell UI is in a XIB):

class AnswerViewCell: UITableViewCell {
	// ...

    func button1Tapped() {
		// index is a field set up on construction
		delegate?.answerCellTapped(index, 1)
	}

    func button2Tapped() {
		// index is a field set up on construction
		delegate?.answerCellTapped(index, 2)
	}

    func button3Tapped() {
		// index is a field set up on construction
		delegate?.answerCellTapped(index, 3)
	}

	// ...
}

In this example, we’ll have the UITableView serve as the delegate and collect the information from the individual AnswerViewCells:

class AnswerCellTableView: UITableViewDataSource, AnswerCellViewDelegate {
    // ...
    
    func answerCellClicked(answerCell: AnswerCellView, answer: UIButton) {
        // do the work to keep track of which buttons have been clicked
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        var answerCell: AnswerCellView
        // construct your AnswerCellView, setting the delegate property on your cell to self
        // ...
        answerCell.delegate = self
        // ...
    }
    
    // ...
}

You could also have your main UIViewController or a view model object implement the delegate, depending on your app’s architecture.

When you need to collect that information from the AnswerCellTableView, you can either provide an accessor method that the UIViewController can use to get the selected answers, or if you need to receive updates in real-time (such as to commit to an external data store), make your UIViewController the delegate, or if you want to keep proper code separation, repeat the delegate pattern and have the UIViewController give the AnswerCellTableView a delegate object. It’s delegates all the way down!

Comments are closed.