Skip to content

Instantly share code, notes, and snippets.

@abhi21git
Last active March 19, 2022 09:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save abhi21git/878eff7203e864dbde8c7072e896427a to your computer and use it in GitHub Desktop.
Save abhi21git/878eff7203e864dbde8c7072e896427a to your computer and use it in GitHub Desktop.
import UIKit
// Confirm this to your cell/header if using Prototype cell
public protocol ReusableView: AnyObject {
static var defaultReuseIdentifier: String { get }
}
extension ReusableView where Self: UIView {
public static var defaultReuseIdentifier: String {
return String(describing: self)
}
}
// Confirm this to your cell/header if using custom XIB
public protocol NibLoadableView: AnyObject {
static var nibName: String { get }
}
extension NibLoadableView where Self: UIView, Self: ReusableView {
static var nibName: String {
return String(describing: self)
}
}
// UITableView + Reusable cell
extension UITableView {
func register<T: UITableViewCell>(_: T.Type) where T: ReusableView {
register(T.self, forCellReuseIdentifier: T.defaultReuseIdentifier)
}
func register<T: UITableViewCell>(_: T.Type) where T: ReusableView, T: NibLoadableView {
let bundle = Bundle(for: T.self)
let nib = UINib(nibName: T.nibName, bundle: bundle)
register(nib, forCellReuseIdentifier: T.defaultReuseIdentifier)
}
func dequeueReusableCell<T: UITableViewCell>(for indexPath: IndexPath) -> T where T: ReusableView, T: NibLoadableView {
register(T.self)
guard let cell = dequeueReusableCell(withIdentifier: T.defaultReuseIdentifier, for: indexPath) as? T else {
fatalError("Could not dequeue cell with identifier: \(T.defaultReuseIdentifier)")
}
return cell
}
func dequeueReusableCell<T: UITableViewCell>(for indexPath: IndexPath) -> T where T: ReusableView {
register(T.self)
guard let cell = dequeueReusableCell(withIdentifier: T.defaultReuseIdentifier, for: indexPath) as? T else {
fatalError("Could not dequeue cell with identifier: \(T.defaultReuseIdentifier)")
}
return cell
}
func registerReusableHeaderFooterView<T: UITableViewHeaderFooterView>(_: T.Type) where T: ReusableView, T: NibLoadableView {
let bundle = Bundle(for: T.self)
let nib = UINib(nibName: T.nibName, bundle: bundle)
register(nib, forHeaderFooterViewReuseIdentifier: T.defaultReuseIdentifier)
}
func dequeueReusableHeaderFooterView<T: UITableViewHeaderFooterView>() -> T where T: ReusableView {
register(T.self, forHeaderFooterViewReuseIdentifier: T.defaultReuseIdentifier)
guard let cell = dequeueReusableHeaderFooterView(withIdentifier: T.defaultReuseIdentifier) as? T else {
fatalError("Could not dequeue Reusable HeaderFooterView with identifier: \(T.defaultReuseIdentifier)")
}
return cell
}
func dequeueReusableHeaderFooterView<T: UITableViewHeaderFooterView>() -> T where T: ReusableView, T: NibLoadableView {
registerReusableHeaderFooterView(T.self)
guard let cell = dequeueReusableHeaderFooterView(withIdentifier: T.defaultReuseIdentifier) as? T else {
fatalError("Could not dequeue Reusable HeaderFooterView with identifier: \(T.defaultReuseIdentifier)")
}
return cell
}
}
// UICollectionView + Reusable Cell
extension UICollectionView {
func register<T: UICollectionViewCell>(_: T.Type) where T: ReusableView {
register(T.self, forCellWithReuseIdentifier: T.defaultReuseIdentifier)
}
func register<T: UICollectionReusableView>(_: T.Type, forSupplementaryViewOfKind kind: String) where T: ReusableView {
register(T.self, forSupplementaryViewOfKind: kind, withReuseIdentifier: T.defaultReuseIdentifier)
}
func register<T: UICollectionViewCell>(_: T.Type) where T: ReusableView, T: NibLoadableView {
let bundle = Bundle(for: T.self)
let nib = UINib(nibName: T.nibName, bundle: bundle)
register(nib, forCellWithReuseIdentifier: T.defaultReuseIdentifier)
}
func dequeueReusableCell<T: UICollectionViewCell>(for indexPath: IndexPath) -> T where T: ReusableView {
register(T.self)
guard let cell = dequeueReusableCell(withReuseIdentifier: T.defaultReuseIdentifier, for: indexPath) as? T else {
fatalError("Could not dequeue cell with identifier: \(T.defaultReuseIdentifier)")
}
return cell
}
func dequeueReusableCell<T: UICollectionViewCell>(for indexPath: IndexPath) -> T where T: ReusableView, T: NibLoadableView {
register(T.self)
guard let cell = dequeueReusableCell(withReuseIdentifier: T.defaultReuseIdentifier, for: indexPath) as? T else {
fatalError("Could not dequeue cell with identifier: \(T.defaultReuseIdentifier)")
}
return cell
}
func dequeueReusableSupplementaryView<T: UICollectionReusableView>(ofKind kind: String, for indexPath: IndexPath) -> T where T: ReusableView {
register(T.self, forSupplementaryViewOfKind: kind)
guard let cell = dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: T.defaultReuseIdentifier, for: indexPath) as? T else {
fatalError("Could not dequeue reusable supplementaryView with identifier: \(T.defaultReuseIdentifier)")
}
return cell
}
}

Prerequisite: Before you start anything and blame me for the crashs make sure your cell class, XIB name and reuse identifier is same.
If you are using Prototpe cell only confirm ReusableView
If using a Custom cell with XIB confirm your cell to both protocols ReusableView & NibLoadableView

class CustomTableViewCell: UITableViewCell, ReusableView, NibLoadableView {

    override func awakeFromNib() {
        super.awakeFromNib()

    }
    
}

Now coming to the actual benefit of this no need to register cells 🤩🤩🤩


So lets come to dequeueing cells

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell: CustomTableViewCell = tableView.dequeueReusableCell(for: indexPath)
        return cell
    }

Similarly for header or footer

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let header: CusttomTableHeaderCell = tableView.dequeueReusableHeaderFooterView()
        return header
    }

Look how in both cases while dequeueing we type casted our cell, without doing this you won't be able to access properties of your custom cell, gone are the days of doing as! CustomTableViewCell after dequeue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment