注册和退出特定类型的UITableViewCell

时间:2019-05-03 06:02:53

标签: ios swift uitableview generics

UITableView是在objc已经“旧”且从未提及过swift的情况下构建的。在objc中,如果您将一个单元出队以进行转换,则只需分配它就可以了,只要您拥有正确的单元,一切都会正常进行。

泛型的力量迅速涌入了iOS(和其他Apple)平台。

现在,我发现自己一次又一次地写很多样板(定义标识符,转换单元格,使用致命错误的力解开等等。)

所以我想知道在代码中是否有一些想法可以使它更容易使用和更简洁。

2 个答案:

答案 0 :(得分:1)

@@ Saren,您的解决方案对于单个UITableViewCell可以很好地工作,但是我对其进行了一些增强,可以支持多个UITableViewCell注册。

UITableView + extension.swift

public protocol ClassNameProtocol {
    static var className: String { get }
    var className: String { get }
}

public extension ClassNameProtocol {
    public static var className: String {
        return String(describing: self)
    }

    public var className: String {
        return type(of: self).className
    }
}

extension NSObject: ClassNameProtocol {}


public extension UITableView {
    public func register<T: UITableViewCell>(cellType: T.Type) {
        let className = cellType.className
        let nib = UINib(nibName: className, bundle: nil)
        register(nib, forCellReuseIdentifier: className)
    }

    public func register<T: UITableViewCell>(cellTypes: [T.Type]) {
        cellTypes.forEach { register(cellType: $0) }
    }

    public func dequeueReusableCell<T: UITableViewCell>(with type: T.Type, for indexPath: IndexPath) -> T {
        return self.dequeueReusableCell(withIdentifier: type.className, for: indexPath) as! T
    }

    public func registerHeaderFooter<T: UITableViewHeaderFooterView>(HeaderFooterType: T.Type) {
        let className = HeaderFooterType.className
        let nib = UINib(nibName: className, bundle: nil)
        register(nib, forHeaderFooterViewReuseIdentifier: className)
    }

    public func registerHeaderFooter<T: UITableViewHeaderFooterView>(HeaderFooterTypes: [T.Type]) {
        HeaderFooterTypes.forEach { registerHeaderFooter(HeaderFooterType: $0) }
    }
}

使用方式

fileprivate let arrCells = [ContactTableViewCell.self,ContactDetailsCell.self]
self.register(cellTypes: arrCells)

注意:请确保您的类名和UITableviewCell的可重用标识符应该相同。

答案 1 :(得分:0)

解决此问题的一种简单方法是编写一个小的扩展名。

如果出队的单元尚未注册,则此解决方案将导致致命错误。如果我们尚未注册单元格,则调用dequeueReusableCell(withIdentifier:for:),这已经是iOS的默认行为。

为完成这项工作,我们需要一种方法来为将使用泛型注册和出队的任何类型的单元格创建唯一标识符。如果您希望有一种方法可以使同一单元出队以使用不同的标识符,那么您将必须使用默认系统(永远不需要)。

因此,让我们创建一个名为UITableView+Tools.swift的类(或您想要命名的任何类)。

extension UITableView {
    private func reuseIndentifier<T>(for type: T.Type) -> String {
        return String(describing: type)
    }

    public func register<T: UITableViewCell>(cell: T.Type) {
        register(T.self, forCellReuseIdentifier: reuseIndentifier(for: cell))
    }

    public func register<T: UITableViewHeaderFooterView>(headerFooterView: T.Type) {
        register(T.self, forHeaderFooterViewReuseIdentifier: reuseIndentifier(for: headerFooterView))
    }

    public func dequeueReusableCell<T: UITableViewCell>(for type: T.Type, for indexPath: IndexPath) -> T {
        guard let cell = dequeueReusableCell(withIdentifier: reuseIndentifier(for: type), for: indexPath) as? T else {
            fatalError("Failed to dequeue cell.")
        }

        return cell
    }

    public func dequeueReusableHeaderFooterView<T: UITableViewHeaderFooterView>(for type: T.Type) -> T {
        guard let view = dequeueReusableHeaderFooterView(withIdentifier: reuseIndentifier(for: type)) as? T else {
            fatalError("Failed to dequeue footer view.")
        }

        return view
    }
}

所以现在我们在我们的类(即视图控制器)中要做的就是注册单元格(不需要标识符)并使其出队(无需标识符,没有强制转换,强制展开或使用警卫人员手动展开)

func viewDidLoad {
    ...

    tableView.register(MyCustomCell.self)
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
    let cell = dequeueReusableCell(forType: MyCustomCell.self, for: indexPath)
    cell.viewModel = cellModel(for: indexPath)

    return cell
}

就是这样。希望你喜欢这个主意。任何其他(更好或更糟)的想法都可以接受。