Cleaner way to handle UITableViewCell

Its really funny and confusing when you start to implement the cellForRow or itemForRow datasource method.  Usually we do that in the ViewController, or in a special dataSource object, and you find yourself asking where should you add that piece of code responsible for configuring or handling the Cell, you would then add it in the Cell subclass itself or just bring more code to the ViewController that is barely controlling his direct view(s).

In a non-trivial world you won’t just set the title and description for the Cell, ending up with 2 to 3 lines, no!, there will be buttons, images , special layout , events and a whole set of operations going on, that is when you handle that in the ViewController it’d be messy and looks so wrong!!!

MVC

Yup , Just like UIViewController , we need a UITableViewCellController, its a bit long name so we will use CellController , since it can be a table view cell or collection view cell, or any list based views element.

WHY

simply to isolate the mess of handling a cell.

How does it look like?

class UserCellController {

let cell: UserTableViewCell
let model: UserModel

init(cell: UserTableViewCell, model: UserModel) {
self.model  = model
self.cell  = cell
}

func configure() {
fill()
style()
}

// here we fill view items

private func fill() {
//e.g. namelabel.text = model.name
}

private func style() {
//e.g. namelabel.cornerRadius = 10
}

}

Inside cellForITem or the method you handle the filling the cell. 

let model = ….

let cell = …

let cellcontroller = UserCellController(cell: cell , model: model)

cellcontroller.configure()

Now you can fill,configure and style the cell based on the model, you can validate the model and modify the input or anything else inside the CellController instead of the tableView datasource method.

As you noted there is a loose end here, as the CellController object should not live in memory for long, and its pointing strongly to the cell object, given the same cell object should be reused and filled with other content.

Based on how many cells can be visible, the table will allocate certain number of cells and keep reusing them.

To solve that we will keep these controllers a live by adding them to dictionary inside the Viewcontroller in our case.

private var controllers: [Int: UserCellController] = [:]

and inside cellForRow…

let cellcontroller = UserCellController(cell: cell , model: model)

cell.configure()

self.controllers[cell.hash] = controller

The trick here is to use cell.hash as there are a fixed number of hashes since cells are reusable inside a tableView .

Actions

now we can handle filling and styling the cell easily , what about actions coming from the Cell itself, what if you have a button inside the cell and you want to handle it inside the ViewController since its probably something bigger than our little CellController can handle.

In that case , it is as simple as adding completion handler to the CellController constructor, or add call back as property , or  Streams as in RxSwift as property , or delegate Oo (just kidding,don’t) …

 

FINALE

That should be it , you got yourself a separate controller that can handle list items more cleanly than dumping all the functionality inside the tableView datasource since life is not that simple and you will need to have a lot of functionalities handled by the Cell.

 

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.