Swift数组中的泛型协议类类型

3
我们已经实现了一个名为Reusable的协议,用于简化UITableView注册/出列实现,适用于我们的UITableViewCell
protocol Reusable: class {
    static var defaultIdentifier: String { get }
}

extension Reusable where Self: UITableViewCell {
    static var defaultIdentifier: String {
        return String(describing: self)
    }
}

class TestTableViewCell: UITableViewCell, Reusable { }
class AnotherTestTableViewCell: UITableViewCell, Reusable { }

然后,有一个扩展到UITableView的类似:

extension UITableView {
    func register<T: UITableViewCell & Reusable>(_: T.Type) {
        register(UINib(nibName: T.defaultIdentifier, bundle: nil), forCellReuseIdentifier: T.defaultIdentifier)
    }
}

以及它的使用:

let tableView: UITableView = UITableView()
tableView.register(TestTableViewCell.self)
tableView.register(AnotherTableViewCell.self)

一切正常,但我们希望将这些类型移动到数组中以进行排序。这是我们遇到问题的地方,以下代码不起作用:

let viewCells = [TestTableViewCell.self, AnotherTestTableViewCell.self]
// Without type annotation, it's [UITableViewCell.Type]
// And the error is: Instance method 'register' requires that 'UITableViewCell' conform to 'Reusable'

for viewCell in viewCells {
    tableView.register(viewCell)
}

我们也尝试过:

let viewCells: [Reusable.Type] = ...
// Error: Cannot invoke 'register' with an argument list of type '(Reusable.Type)'

另外还有这个:
let viewCells: [(UITableViewCell & Reusable).Type] = ...
// Error: Instance method 'register' requires that 'UITableViewCell' conform to 'Reusable'

有没有一种方法可以将类类型信息与协议一致性存储在数组中,以使其正常工作?

你使用的是哪个版本的Swift? - Vyacheslav
@Vyacheslav 4.2 - EDUsta
什么问题?它没有编译吗?我在 Playground 中尝试了一下,它可以编译。 - Rico Crescenzio
@RicoCrescenzio 是的,我已经编辑了问题,因为数组中只有一种类类型。它不应该编译。 - EDUsta
1
但我们想将这些类型移动到一个数组中,这可能会有些困难。Swift 不支持元类型的数组概念;在 Swift 中,元类型并不是真正的类型。您可能会更喜欢使用其他编程语言或其他方法来实现整体目标。 - matt
@matt 感谢你提供的信息,虽然这不是一个必须的问题,但如果能够解决就更好了。 - EDUsta
4个回答

5

只需为UITableViewCell编写一个扩展,使其符合Reusable协议,并且不需要将单元格强制转换为任何类型:

extension UITableViewCell: Reusable {}


P.S. 你还可以在iOS上检查最受欢迎的可重用协议实现,点击这里


祝你好运 :)


不幸的是,它无法工作:在强制转换中,“UITableViewCell.Type”的值不符合“UITableViewCell&Reusable”的要求。 - EDUsta
@EDUsta 为什么你不想让每个 UITableViewCell 都遵循这个协议呢? - JeremyP
在这种情况下,为什么不直接扩展你认可的UITableViewCell子类呢? - JeremyP
@JeremyP 你是指作为超类吗?如果是的话,我不太喜欢那样做。无论如何,问题的主要点在于我们如何在数组中保持类类型。你有什么想法吗,还是不可能实现? - EDUsta
@JeremyP 确实,我们完全按照问题要求做了。 - EDUsta
显示剩余4条评论

2
protocol Reusable: class {
    static var defaultIdentifier: String { get }
}

extension Reusable where Self: UITableViewCell {
    static var defaultIdentifier: String {
        return String(describing: self)
    }
}

class TestTableViewCell: UITableViewCell, Reusable { }
extension UITableView {
    func register<T: UITableViewCell & Reusable>(_: T.Type) {
        register(UINib(nibName: T.defaultIdentifier, bundle: nil), forCellReuseIdentifier: T.defaultIdentifier)
    }
}
let tableView: UITableView = UITableView()
tableView.register(TestTableViewCell.self)
let viewCells = [TestTableViewCell.self]
// Without type annotation, it's [UITableViewCell.Type]
// And the error is: Instance method 'register' requires that 'UITableViewCell' conform to 'Reusable'

for viewCell in viewCells {
    tableView.register(viewCell)
}

这段代码成功执行。原始答案翻译成“最初的回答”。

谢谢您的回答,我没有看到任何区别,是关于Swift 4.2/5的吗? - EDUsta
更新:当只有一个类时,它可以编译,但如果有多个类,则无法编译。已更新问题。 - EDUsta

1
希望您对此感到满意。关键是协议中的 Self 关键字。
protocol Reusable: class {
    static var defaultIdentifier: String { get }
    static func register(tableView: UITableView)
}

extension Reusable where Self: UITableViewCell {
    static var defaultIdentifier: String {
        return String(describing: self)
    }

    static func register(tableView: UITableView) {
        tableView.register(UINib(nibName: Self.defaultIdentifier, bundle: nil), forCellReuseIdentifier: Self.defaultIdentifier)
    }
}

extension UITableView {
    func register(_ reusable: Reusable.Type) {
        reusable.register(tableView: self)
    }
}


let viewCells: [Reusable.Type] = [TestTableViewCell.self, AnotherTestTableViewCell.self]

for viewCell in viewCells {
    tableView.register(viewCell)
}

我建议您阅读以下链接:


@EDUsta 听起来不错。如果您不介意的话,请接受我的答案。 - Yonguk Jeong

0
只需要使用这个表视图扩展方法来注册单元格即可。
 extension UITableView {
     func registerCell<T: UITableViewCell>(cellType: T.Type) {
        let nib = UINib(nibName: String(describing: cellType.self), bundle: nil)
        let reuseIdentifier = String(describing: cellType.self)
        self.register(nib, forCellReuseIdentifier: reuseIdentifier)
     }
} 

然后像这样使用它

 let tableView: UITableView = UITableView()
    let cellsArray = [Test.self, Test1.self, Test2.self]
    for cell in cellsArray {
        tableView.registerCell(cellType: cell)
    }

谢谢您的回复,但它只是忽略了协议,这正是我们进行此操作的全部原因。 - EDUsta

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接