UIKit
s API for registering UINib
s to UICollectionView
s looks bad. Just look at this.
sizeCollectionView.register(UINib(nibName: "SearchFilterSizeCell", bundle: nil), forCellWithReuseIdentifier: "sizeCell")
Using them again is even worse;
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "sizeCell", for: indexPath) as! SearchFilterSizeCell
Ew.
So why not Swift it up and make it look nice? I couldn't find a reason not to, so here's a neat protocol.
/**
A convenience protocol to register and instaniate cells from an XIB programatically cleaner than the native function calls.
*/
protocol Nibable: class where Self: UICollectionReusableView {
/// The reuse identifier to register for / dequeue with
static var id: String { get }
/// The name of the .xib file. Must contains ONE top level UICollectionViewCell view with its reuseIdentifier set to equal the id provided to this protocol.
static var nibName: String { get }
}
extension UICollectionView {
/**
Convenience function to dequeue a UICollectionViewCell from a subclass which conforms to Nibable.
**Important**
This function force unwraps the result and will crash if the cell class doesn't properly conform to Nibable.
- parameter cellClass: Class of the cell to dequeue.
- parameter ofKind: An optional parameter to indicate that we should dequeue a supplementary reusable view.
- parameter indexPath: The index path specifying the location of the cell. The data source receives this information when it is asked for the cell and should just pass it along. This method uses the index path to perform additional configuration based on the cell’s position in the collection view.
- returns: A dequeued cell cast to the Nibable conformer.
*/
func dequeue<CellClass: Nibable>(_ cellClass: CellClass.Type, ofKind kind: String? = nil, for indexPath: IndexPath) -> CellClass {
if let kind = kind {
return dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: cellClass.id, for: indexPath) as! CellClass
} else {
return dequeueReusableCell(withReuseIdentifier: cellClass.id, for: indexPath) as! CellClass
}
}
/**
Convenience function to register a UICollectionViewCell subclass which conforms to Nibable.
- parameter cellClass: Class of the cell to register.
- parameter ofKind: An optional parameter to indicate that we should dequeue a supplementary reusable view.
*/
func register<CellClass: Nibable>(_ cellClass: CellClass.Type, ofKind kind: String? = nil) {
if let kind = kind {
register(UINib(nibName: cellClass.nibName, bundle: nil),
forSupplementaryViewOfKind: kind,
withReuseIdentifier: cellClass.id)
} else {
register(UINib(nibName: cellClass.nibName, bundle: nil),
forCellWithReuseIdentifier: cellClass.id)
}
}
}
class Cell: UICollectionViewCell, Nibable {
static let id: String = "cell"
static let nibName: String = "Cell"
}
class ViewController {
func viewDidLoad() {
collectionView.register(Cell.self)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
return collectionView.dequeue(Cell.self, for: indexPath)
}
}