英文:
Getting rid of strings as reuse identifiers (AnyClass issue)
问题
使用字符串标识符来重用单元格会导致很多问题...我尝试摆脱它们(以这里描述的方式: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 {}
它运行正常:
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)
}
但我尝试更进一步 - 通过使用仅接受单元格类并执行所有操作的函数:
extension UICollectionView {
// 错误:在调用实例方法“register”时没有精确匹配...
func register(_ cellClass: ReuseIdentifiable) {
register(
cellClass.self,
forCellWithReuseIdentifier: type(of: cellClass).reuseIdentifier
)
}
// 一切正常
func dequeueReusableCell(_ ofType: ReuseIdentifiable,
for indexPath: IndexPath) -> UICollectionViewCell {
return dequeueReusableCell(
withReuseIdentifier: type(of: ofType).reuseIdentifier, for: indexPath)
}
}
Apple文档说明:
所有类类型隐式遵循的协议。
我理解协议ReuseIdentifiable
和AnyClass
是完全不同的。我尝试像这样声明我的协议:
protocol ReuseIdentifiable: AnyClass { // 在此处编写代码 }
但它引发错误:
从非协议、非类类型“AnyClass”(也称为“any AnyObject.Type”)继承
如何将我的cellClass
传递给UICollectionView.dequeueReusableCell
函数?因为除了扩展之外的任何其他地方都可以成功传递:
override func viewDidLoad() {
super.viewDidLoad()
collectionView.register(
GroupsViewCell.self,
forCellWithReuseIdentifier: GroupsViewCell.reuseIdentifier
)
collectionView.delegate = self
collectionView.dataSource = self
collectionLayout.layoutItems(in: self.collectionView)
}
谢谢您的支持。
英文:
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.
答案1
得分: 1
以下是翻译好的内容:
您可以按照以下方式实现您想要的内容:
public protocol ReuseIdentifiable: UIView {
static var reuseIdentifier: String { get }
}
请注意,在静态属性扩展中的“Self”前缀是多余的,因此可以省略。
public extension ReuseIdentifiable {
static var reuseIdentifier: String { .init(describing: self) }
}
这将使 UITableViewCell
和 UITableViewHeaderFooterView
遵循您的协议:
extension UITableViewCell: ReuseIdentifiable { }
extension UITableViewHeaderFooterView: ReuseIdentifiable { }
现在,您可以创建扩展 UITableView
的通用方法,如下所示:
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
}
}
UICollectionView
的方法实现将留给问题提出者作为练习。
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论