Skip to content

Instantly share code, notes, and snippets.

@IanKeen
Last active July 10, 2019 22:02
Show Gist options
  • Save IanKeen/4ecbffea1e9d0c1bf1d6 to your computer and use it in GitHub Desktop.
Save IanKeen/4ecbffea1e9d0c1bf1d6 to your computer and use it in GitHub Desktop.
Small utility methods to simplify dealing with Reusable items i.e. table/collection view cells
//`UITableViewCell` and `UICollectionViewCell` are `Reusable` by defaut
//Use the extension method to dequeue an instance of the appropriate `Reusable`
class MyVC: UITableViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
tableView
.registerReusable(FooCell.self)
.registerReusable(BarCell.self)
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: FooCell = tableView.dequeueReusable(indexPath)
return cell
}
}
//Thats it!
protocol Reusable: class {
static var reuseIdentifier: String { get }
}
extension Reusable {
static var reuseIdentifier: String {
return String(self)
}
}
extension UITableViewCell: Reusable { }
extension UITableViewHeaderFooterView: Reusable { }
extension UICollectionViewCell: Reusable { }
extension UITableView {
//MARK: - Cells
func registerReusable(cellClass: Reusable.Type, fromNib: Bool = true) -> UITableView {
if (fromNib) {
let nib = UINib(nibName: cellClass.reuseIdentifier, bundle: nil)
self.registerNib(nib, forCellReuseIdentifier: cellClass.reuseIdentifier)
} else {
self.registerClass(cellClass, forCellReuseIdentifier: cellClass.reuseIdentifier)
}
return self
}
func dequeueReusable<T: UITableViewCell where T: Reusable>(indexPath: NSIndexPath) -> T {
return self.dequeueReusableCellWithIdentifier(T.reuseIdentifier, forIndexPath: indexPath) as! T
}
//MARK: - HeaderFooter
func registerReusableHeaderFooterView(headerFooterViewClass: Reusable.Type, fromNib: Bool = true) -> UITableView {
if (fromNib) {
let nib = UINib(nibName: headerFooterViewClass.reuseIdentifier, bundle: nil)
self.registerNib(nib, forHeaderFooterViewReuseIdentifier: headerFooterViewClass.reuseIdentifier)
} else {
self.registerClass(headerFooterViewClass, forHeaderFooterViewReuseIdentifier: headerFooterViewClass.reuseIdentifier)
}
return self
}
func dequeueReusableHeaderFooterView<T: UITableViewHeaderFooterView where T: Reusable>() -> T? {
return self.dequeueReusableHeaderFooterViewWithIdentifier(T.reuseIdentifier) as? T
}
}
extension UICollectionView {
//MARK: - Cells
func registerReusable(cellClass: Reusable.Type, fromNib: Bool = true) -> UICollectionView {
if (fromNib) {
let nib = UINib(nibName: cellClass.reuseIdentifier, bundle: nil)
self.registerNib(nib, forCellWithReuseIdentifier: cellClass.reuseIdentifier)
} else {
self.registerClass(cellClass, forCellWithReuseIdentifier: cellClass.reuseIdentifier)
}
return self
}
func dequeueReusable<T: UICollectionViewCell where T: Reusable>(indexPath: NSIndexPath) -> T {
return self.dequeueReusableCellWithReuseIdentifier(T.reuseIdentifier, forIndexPath: indexPath) as! T
}
}
@IanKeen
Copy link
Author

IanKeen commented Apr 5, 2016

@winkelsdorf : Wow - I hadn't looked back here in a while. 👍

  • Thanks for adding the header/footer implementations and the class based handling ( I personally only use nibs which is why I omitted it originally 😄 )
  • You're right, I hadn't looked into the overhead of the automatic and constant registering of cells. I've used this pretty extensively however and (as yet) have not noticed any performance hits. I do agree that its probably misleading though to automatically register things when the function name makes no mention of this.
  • I've left the functions names as is - I think it could lead to confusion having something like dequeueClass(fromNib: true) I'd rather keep it simple
  • I generally try and avoid adding things like T.Type to the func - the 'noise' of having to include it can't be avoided or inferred in this instance - my preference is to have it as part of the declaration rather than the function (I just feel thats where it belongs)

I've updated the snippet with these things in mind, thanks for the feedback!

@hitendradeveloper
Copy link

Nice Implementation.! (Y) +1

@winkelsdorf
Copy link

winkelsdorf commented Oct 5, 2016

@IanKeen Sorry missed your notification. Guess the notification system's not working with Gists :(

You're very welcome. Yes, both approaches - with and T.Type are valid and have their benefits. Depends on your general code style. I just wanted to add and share my thoughts and improvements - glad you incorporated some of them :)

Here's the recent update for Swift 3, if anybody needs it:

import UIKit

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

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

extension UITableViewCell: Reusable { }
extension UITableViewHeaderFooterView: Reusable { }
extension UICollectionViewCell: Reusable { }

// MARK: - UITableViewCell
public extension UITableView {

    @discardableResult
    public func registerReusableClass(withClass cellClass: Reusable.Type) -> UITableView {
        register(cellClass, forCellReuseIdentifier: cellClass.reuseIdentifier)

        return self
    }

    @discardableResult
    public func registerReusableNib(withClass cellClass: Reusable.Type) -> UITableView {
        let nib = UINib(nibName: cellClass.reuseIdentifier, bundle: nil)
        register(nib, forCellReuseIdentifier: cellClass.reuseIdentifier)

        return self
    }

    public func dequeueReusableCell<T: UITableViewCell>(withClass cellClass: T.Type, for indexPath: IndexPath) -> T where T: Reusable {
        // swiftlint:disable:next force_cast
        return dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as! T
    }

}

// MARK: - UITableViewHeaderFooterView
public extension UITableView {

    @discardableResult
    public func registerReusableHeaderFooterView(withClass headerFooterViewClass: Reusable.Type, fromNib: Bool = false) -> UITableView {
        if fromNib {
            let nib = UINib(nibName: headerFooterViewClass.reuseIdentifier, bundle: nil)
            register(nib, forHeaderFooterViewReuseIdentifier: headerFooterViewClass.reuseIdentifier)
        } else {
            register(headerFooterViewClass, forHeaderFooterViewReuseIdentifier: headerFooterViewClass.reuseIdentifier)
        }

        return self
    }

    public func dequeueReusableHeaderFooterView<T: UITableViewHeaderFooterView>(withClass headerFooterViewClass: T.Type = T.self) -> T? where T: Reusable {
        return dequeueReusableHeaderFooterView(withIdentifier: T.reuseIdentifier) as? T
    }

}

// MARK: - UICollectionView
extension UICollectionView {

    @discardableResult
    public func registerReusable(withClass cellClass: Reusable.Type, fromNib: Bool = false) -> UICollectionView {
        if fromNib {
            let nib = UINib(nibName: cellClass.reuseIdentifier, bundle: nil)
            register(nib, forCellWithReuseIdentifier: cellClass.reuseIdentifier)
        } else {
            register(cellClass, forCellWithReuseIdentifier: cellClass.reuseIdentifier)
        }

        return self
    }

    public func dequeueReusableCell<T: UICollectionViewCell>(withClass cellClass: T.Type, for indexPath: IndexPath) -> T where T: Reusable {
        // swiftlint:disable:next force_cast
        return dequeueReusableCell(withReuseIdentifier: T.reuseIdentifier, for: indexPath) as! T
    }

}

@IanKeen
Copy link
Author

IanKeen commented Oct 22, 2016

Awesome! thanks for the updates 😄

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