frameworker
frameworker

Reputation: 402

problems to get correct reuseIdentifier

I've created an extension for UITableView and want to get the correct reuseIdentifier at runtime, but I can't:

extension UITableView {

func dequeueReusableCell<T: UITableViewCell>(for indexPath: IndexPath) -> T {

    let s = T.reuseIdentifier

The variable "s" always contains "UITableViewCell" and not the name which I did specify within the storyboard.

Upvotes: 1

Views: 875

Answers (2)

vadian
vadian

Reputation: 285082

Never mind, the whole method

extension UITableView {

    func dequeueReusableCell<T: UITableViewCell>(for indexPath: IndexPath) -> T {
        return self.dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as! T 
    }
}

is supposed to work correctly.


An alternative is a protocol extension and static methods, just adopt the protocol in the UITableViewCell subclasses.

protocol Reusable {
    associatedtype CellType : UITableViewCell = Self

    static var cellIdentifier : String { get }
    static func dequeueReusableCell(in tableView : UITableView, for indexPath: IndexPath) -> CellType
}

extension Reusable where Self : UITableViewCell {

    static var cellIdentifier : String {
        return String(describing: Self.self)
    }

    static func dequeueReusableCell(in tableView : UITableView, for indexPath: IndexPath) -> CellType {
        return tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! CellType
    }
}

And call it

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

Upvotes: 1

Infinity James
Infinity James

Reputation: 4735

My way of handling this is as follows.

I create a protocol which declares a type as loadable from a nib:

//  MARK: Nib Loadable
public protocol NibLoadable {
    /// The name of the .xib file in which this view is defined.
    static var nibName: String { get }
}
//  MARK: Nib Loadable Convenience
public extension NibLoadable where Self: UIView {
    static var nibName: String { return NSStringFromClass(self).components(separatedBy: ".").last! }
}

I create another type which declares a type as reusable and automatically conform table and collection view cells:

//  MARK: Reusable View
/**
Indicates that a view can be reused provided it matches a provided identifier.
*/
public protocol ReusableView: class {
    /// The identifier for this type of reusable view.
    static var defaultReuseIdentifier: String { get }
}
//  MARK: Reusable View Convenience
public extension ReusableView where Self: UIView {
    static var defaultReuseIdentifier: String { return NSStringFromClass(self).components(separatedBy: ".").last! }
}
extension UICollectionViewCell: ReusableView { }
extension UITableViewCell: ReusableView { }

I then create an easy means with which to register such a cell:

//  MARK: Registration
public extension UICollectionView {
    func register<T: UICollectionViewCell>(_: T.Type) {
        register(T.self, forCellWithReuseIdentifier: T.defaultReuseIdentifier)
    }
    func register<T: UICollectionViewCell>(_: T.Type) where T: NibLoadable {
        let bundle = Bundle(for: T.self)
        let nib = UINib(nibName: T.nibName, bundle: bundle)
        register(nib, forCellWithReuseIdentifier: T.defaultReuseIdentifier)
    }
}
public extension UITableView {
    func register<T: UITableViewCell>(_: T.Type) {
        register(T.self, forCellReuseIdentifier: T.defaultReuseIdentifier)
    }
    func register<T: UITableViewCell>(_: T.Type) where T: NibLoadable {
        let bundle = Bundle(for: T.self)
        let nib = UINib(nibName: T.nibName, bundle: bundle)
        register(nib, forCellReuseIdentifier: T.defaultReuseIdentifier)
    }
}

Finally I have a convenient way of dequeuing these cells:

//  MARK: Dequeuing
public extension UICollectionView {
    func dequeue<T: UICollectionViewCell>(for indexPath: IndexPath) -> T {
        guard let cell = dequeueReusableCell(withReuseIdentifier: T.defaultReuseIdentifier, for: indexPath) as? T else {
            fatalError("Could not dequeue cell with identifier: \(T.defaultReuseIdentifier)")
        }
        return cell
    }
}
public extension UITableView {
    func dequeue<T: UITableViewCell>(for indexPath: IndexPath) -> T {
        guard let cell = dequeueReusableCell(withIdentifier: T.defaultReuseIdentifier, for: indexPath) as? T else {
            fatalError("Could not dequeue cell with identifier: \(T.defaultReuseIdentifier)")
        }
        return cell
    }
}

In use the app I make the reuse identifier for any cell its class name. I then conform the cell like so:

extension PersonalDetailTableViewCell: NibLoadable { }

The owner of the table view will register the cell within viewDidLoad():

tableView.register(PersonalDetailTableViewCell.self)

Within the cellForRow function I'll dequeue appropriately:

let cell: PersonalDetailTableViewCell = tableView.dequeue(for: indexPath)

I have made this available in a gist here.

Upvotes: 0

Related Questions