A Swift mixin to use UITableViewCells
and UICollectionViewCells
in a type-safe way, without the need to manipulate their String
-typed reuseIdentifiers
. This library also supports arbitrary UIView
to be loaded via a XIB using a simple call to loadFromNib()
TL;DR:
- Mark your
UITableViewCell
andUICollectionViewCell
classes to conform to eitherReusable
orNibReusable
(no additional code to implement!) - Then simply use
tableView.dequeueReusableCell(indexPath: indexPath) as MyCustomCell
and you'll get a dequeued instance of the expected cell class in return. No need for you to manipulatereuseIdentifiers
manually!
No more force-casting the returned UITableViewCell
instance down to your MyCustomCell
class, and no more fear that you'll mismatch the reuseIdentifier
and the class you down-cast to. Now all you have is a beautiful code and type-safe cells!
For more information on how this works, see my dedicated blog post about this technique.
Note: the Reusable
library can also be used to mark any arbitrary UIView
as NibLoadable
and then simply create an instance of that XIB-based view using MyCustomView.loadFromNib()
.
First, declare your cells to conform to:
- the
Reusable
protocol if they don't depend on a NIB (this will useregisterClass(…)
to register the cell) - the
NibReusable
protocol if they use aXIB
file for their content (this will useregisterNib(…)
to register the cell)
So for example to create a UITableViewCell
subclass which doesn't use a XIB (either because it is only created via code, or because it will be registered automatically via a storyboard):
class CodeBasedCustomCell: UITableViewCell, Reusable {
// By default this cell will have a reuseIdentifier of "CodeBasedCustomCell"
// unless you provide an alternative implementation of `var reuseIdentifier`
// No need to add anything to conform to Reusable. you can just keep your normal cell code
@IBOutlet private weak var label: UILabel!
func fillWithText(text: String?) { label.text = text }
}
And to create a UITableViewCell
subclass whose content is based on a XIB
:
class NibBasedCustomCell: UITableViewCell, NibReusable {
// Here we provide a nib for this cell class (which, if we don't override the protocol's
// default implementation of `nib`, will use a XIB of the same name as the class)
// No need to add anything to conform to Reusable. you can just keep your normal cell code
@IBOutlet private weak var pictureView: UIImageView!
func fillWithImage(image: UIImage?) { pictureView.image = image }
}
If you create a XIB-based cell, don't forget to set its Reuse Identifier field in Interface Builder to the same string as the name of the cell class itself.
This works exactly the same as UITableViewCell
.
So for a Code-based UICollectionViewCell
subclass:
// A UICollectionViewCell which doesn't need a XIB to register
// Either because it's all-code, or because it's registered via Storyboard
class CodeBasedCollectionViewCell: UICollectionViewCell, Reusable {
// The rest of the cell code goes here
}
And for a XIB-based UICollectionViewCell
subclass:
// A UICollectionViewCell using a XIB to define it's UI
// And that will need to register using that XIB
class NibBasedCollectionViewCell: UICollectionViewCell, NibReusable {
// The rest of the cell code goes here
}
Reusable
can also be used to load an arbitrary UIView
subclass (even a non-reusable, non-cell view) designed in a XIB by simply marking it as NibLoadable
:
class NibBasedRandomView: UIView, NibLoadable {
// The rest of the view code goes here
}
Reusable
can also be used to load a UIView
in Xib's File's Owner (even a non-reusable, non-cell view) designed in a XIB by simply marking it as NibOwnerLoadable
:
class NibBasedRandomView: UIView, NibOwnerLoadable {
// The rest of the view code goes here
}
Then to use those cells, you'll register them like this, without the need to manipulate any reuseIdentifier
anywhere in the code:
class MyViewController: UIViewController {
@IBOutlet private weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.registerReusableCell(CodeBasedCustomCell) // This will register using the class without using a UINib
tableView.registerReusableCell(NibBasedCustomCell) // This will register using NibBasedCustomCell.xib
}
}
Note: If your cell is prototyped in a Storyboard, there is no need to call
registerReusableCell
for those cell prototypes, as the Storyboard auto-register these cell prototypes with theUITableView
/UICollectionView
they are prototyped into.
Then in your implementation of UIViewControllerDataSource
, especially to dequeue a cell you will be able to do:
extension MyViewController: UITableViewDataSource {
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(indexPath: indexPath) as CodeBasedCustomCell
// Customize the cell here. You can call any type-specific methods here without the need for type-casting
cell.fillWithText("Foo")
return cell
} else {
let cell = tableView.dequeueReusableCell(indexPath: indexPath) as NibBasedCustomCell
// Customize the cell here. no need to downcasting here either!
cell.fillWithImage(UIImage(named:"Bar"))
return cell
}
}
}
If you mark an arbitrary (non-cell) UIView
as NibLoadable
as demonstrated above, you can also instantiate such a view using its Nib by calling loadFromNib()
:
let instance1 = NibBasedRandomView.loadFromNib()
let instance2 = NibBasedRandomView.loadFromNib()
let instance3 = NibBasedRandomView.loadFromNib()
…
Reusable
, NibLoadable
and NibReusable
are what is usually called Mixins, which basically is a Swift protocol with a default implementation provided for all of its methods. The main benefit is that you don't need to add any code: just conform to Reusable
, NibLoadable
or NibReusable
and you're ready to go.
But of course, those provided implementations are just default implementations. That means that if you need you can still provide your own implementations in case for some reason some of your cells don't follow the classic configuration of using the same name for both the class, the reuseIdentifier
and the XIB file.
class VeryCustomNibBasedCell: UITableViewCell, NibReusable {
// This cell use a non-standard configuration: its reuseIdentifier and XIB file
// have a different name as the class itself. So we need to provide a custom implementation or `NibReusable`
static var reuseIdentifier: String { return "VeryCustomReuseIdentifier" }
static var nib: UINib { return UINib(nibName: "VeryCustomUI", bundle: nil) } // Use VeryCustomUI.xib
// Then continue with the rest of your normal cell code
}
Note how, in the examples above and when using Reusable
:
-
You never have to use a String-typed
reuseIdentifier
in your code anywhere -
The proper cell type to dequeue is infered by Swift from the return type
- The simple fact that we wrote
dequeueReusableCell(…) as MyCellType
let the Swift compiler infer that you expect aMyCellType
and deduce thereuseIdentifier
to use for that all by itself! ✨
- The simple fact that we wrote
-
The code is type-safe, as the
dequeueReusableCell(…)
function will return the type you asked — and not theUITableViewCell
non-specific superclass as with Apple APIs- This way you can call methods even specific to your
MyCellType
return type on that cell next, without the need to cast it!
- This way you can call methods even specific to your
For more information and explanations on this code, see my detailed blog post about this here.
This repository comes with an example project in the Example/
folder. Feel free to try it.
It demonstrate how Reusable
work:
- both for
UITableViewCell
andUICollectionViewCell
subclasses, - both for cells whose UI template is either only provided by plain code, or provided by a XIB, or prototyped directly in a Storyboard.
- both for cells
UICollectionView
'sSupplementaryViews
(section Headers)
This code is distributed under the MIT license. See the LICENSE
file for more info.