Reputation: 402
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
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
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