Swift Tricks

My own, mostly internal blog of Swift tips and tricks

Custom reusable UITableViewCells

Reusable cells

When you set up reusable cells for your `UITableView` datasource methods it is often designed as part of the table with prototype cells.

The advantage is that these cells are automatically registered with the tableview and do not need any additional setup. The downside is that the cell is connected to this particular table and it may cause issues to try and reuse the cell elsewhere.

So it is generally a better, more modular solution to design the cells independently of the UITableView.

Reuse identifier protocol

To avoid using string literals in your code, you can use reuse identifiers that match the name of your custom class. Let's create a `Reusable` protocol to generalize this functionality:

public protocol Reusable {
    static var reuseIdentifier: String { get }
}

public extension Reusable {
    public static var reuseIdentifier: String {
        return String(describing: self)
    }
}

Build your custom interface

Next up, let's build the custom interface. In Xcode create a UITableViewCell subclass and associated NIB file. Now drag a UITableView cell object onto the canvas area and start designing your interface with Interface Builder.

Connect NIB and class

To ensure that the class and the NIB file are connected, make sure to set the File Owner class in Interface Builder (this may already have been done if you created them at once).

Also make sure that when you hook up outlets and actions, this is done to the file owner and not directly with the class source file.

Finally, make sure to name your class and NIB the same. In Interface Builder use the inspector to set the class of the cell to your own class and also set the reuse identifier to the name of the class.

Register the class with your table view

When registering your cell class (in the TableView) you will need to load up both the nib and the class:

let cellNib = UINib(nibName: MyTableViewCell.reuseIdentifier, bundle: Bundle(for: MyTableViewCell.self))

self.register(cellNib, forCellReuseIdentifier: MyTableViewCell.reuseIdentifier)

The cell is now registered and ready for reuse:

let cell = tableView.dequeueReusableCell(withIdentifier: MyTableViewCell.reuseIdentifier)

Use a base class to simplify the call

If you are registering a lot of cell types, it will help to create a base class (or extension) to simplify the call:

extension UITableView {
    
    /// Register a class and nib
    ///
    /// Use this if your classname, nib name, and reuse identifier are the same.
    /// - Warning: Make sure to set the reuse identifier in the UI.

    public func register(_ myClass: UITableViewCell.Type) {
        let cellNib = UINib(nibName: myClass.reuseIdentifier, bundle: Bundle(for: myClass))
        self.register(cellNib, forCellReuseIdentifier: myClass.reuseIdentifier)
    }

}

// So in your tableview init you can now call:
self.register(MyTableViewCell.self)

// ...and it will load up the Nib and the class as defined above.
// IMPORTANT: always register your class *before* you load any table data (so best to do it during init).