Using string identifiers for reuse cells cause a lot of problems... I tried getting rid of them (in way described here: https://medium.com/bleeding-edge/nicer-reuse-identifiers-with-protocols-in-swift-97d18de1b2df):
protocol ReuseIdentifiable {
static var reuseIdentifier: String { get }
}
extension ReuseIdentifiable {
static var reuseIdentifier: String {
String(describing: Self.self)
}
}
extension UICollectionViewCell: ReuseIdentifiable {}
extension UITableViewCell: ReuseIdentifiable {}
It works fine:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: GroupsViewCell.reuseIdentifier,
for: indexPath
)
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
collectionView.register(
GroupsViewCell.self,
forCellWithReuseIdentifier: GroupsViewCell.reuseIdentifier
)
collectionView.delegate = self
collectionView.dataSource = self
collectionLayout.layoutItems(in: self.collectionView)
}
But I tried to go a bit further - by using functions that take only cell class and do all the stuff:
extension UICollectionView {
// Error: no exact matches in call to instance method 'register'...
func register(_ cellClass: ReuseIdentifiable) {
register(
cellClass.self,
forCellWithReuseIdentifier: type(of: cellClass).reuseIdentifier
)
}
// All ok
func dequeueReusableCell(_ ofType: ReuseIdentifiable,
for indexPath: IndexPath) -> UICollectionViewCell {
return dequeueReusableCell(
withReuseIdentifier: type(of: ofType).reuseIdentifier, for: indexPath)
}
}
Apple documentation tells that:
The protocol to which all class types IMPLICITLY conform.
I understand that protocol ReuseIdentifiable and AnyClass are really different guys.. I tried to declare my protocol like this:
protocol ReuseIdentifiable: AnyClass { // code here }
but it causes error:
Inheritance from non-protocol, non-class type 'AnyClass' (aka 'any AnyObject.Type')
How can I pass my cellClass to UICollectionView.dequeueReusableCell function?
Because in any other place except extension it is being passed successfully:
override func viewDidLoad() {
super.viewDidLoad()
collectionView.register(
GroupsViewCell.self,
forCellWithReuseIdentifier: GroupsViewCell.reuseIdentifier
)
collectionView.delegate = self
collectionView.dataSource = self
collectionLayout.layoutItems(in: self.collectionView)
}
Thank you for support.
You can accomplish what you want as follow:
public protocol ReuseIdentifiable: UIView {
static var reuseIdentifier: String { get }
}
Note that Self
prefix inside a static property extension is redundant therefore can be omitted.
public extension ReuseIdentifiable {
static var reuseIdentifier: String { .init(describing: self) }
}
This will make UITableViewCell
and UITableViewHeaderFooterView
conform to your protocol:
extension UITableViewCell: ReuseIdentifiable { }
extension UITableViewHeaderFooterView: ReuseIdentifiable { }
Now you can create the generic methods extending UITableview
as follow:
public extension UITableView {
func register<T: UITableViewCell>(_ : T.Type) {
register(T.self, forCellReuseIdentifier: T.reuseIdentifier)
}
func register<T: UITableViewHeaderFooterView>(_: T.Type) {
register(T.self, forHeaderFooterViewReuseIdentifier: T.reuseIdentifier)
}
func dequeueReusableCell<T: UITableViewCell>(with identifier: String, for indexPath: IndexPath) -> T {
guard let cell = dequeueReusableCell(withIdentifier: identifier, for: indexPath) as? T else {
fatalError("Error dequeuing cell with identifier: " + identifier)
}
return cell
}
func dequeueReusableCell<T: UITableViewCell>(for indexPath: IndexPath, cellType: T.Type = T.self) -> T {
let dequeueCell = dequeueReusableCell(withIdentifier: cellType.reuseIdentifier, for: indexPath)
guard let cell = dequeueCell as? T else {
fatalError("Error dequeuing cell with identifier: " + cellType.reuseIdentifier)
}
return cell
}
func dequeueReusableHeaderFooterView<T: ReuseIdentifiable>() -> T {
guard let cell = dequeueReusableHeaderFooterView(withIdentifier: T.reuseIdentifier) as? T else {
fatalError("Error dequeuing HeaderFooter with identifier: " + T.reuseIdentifier)
}
return cell
}
}
The UICollectionView
methods implementation will leave for the OP as an excercise.