首页 > 解决方案 > Swift - 不同表格视图单元格的通用模型

问题描述

我正在处理一个表格视图(类似于 Facebook 的 Messenger),其中我有不同的单元格类型(图像、文本、视频等)。我要存档的是,我想声明一个列表message model, 我将配置tableView以便单元格将由模型本身确定和配置。为了做到这一点,我需要以某种方式告诉它与model哪个UITableViewCell类相关联。基本上我想要一个这样的协议:

protocol ChatMessageDisplayable {
   static var myCellType: UITableViewCell { get } //Defines the UITableViewCell class this model is associated with
   func configure(cell: /*the associated cell type*/) // Let the model itself configure the cell.
}

然后我在我的 ViewController 中,我将声明一个数组

messageModels = [ChatMessageDisplayable]

我的 UITableViewDataSource 实现:

public func numberOfSections(in tableView: UITableView) -> Int {
    return messageModels.count
}

public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let model = messageModel[indexPath.row]


    let cellIdentifier = /* Name of the UITableViewCell this model is associated with */


    let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)


    model.configure(cell: cell)
    return cell
}

无论如何我可以归档这个目标吗?

标签: swiftuitableviewgenerics

解决方案


想象一下你的数据会是这样的:

class TableViewModel {
    let items: [Any] = [
        User(name: "John Smith", imageName: "user3"),
        "Hi, this is a message text. Tra la la. Tra la la.",
        Bundle.main.url(forResource: "beach@2x", withExtension: "jpg")!,
        User(name: "Jessica Wood", imageName: "user2"),
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
    ]
}

所以通常我们会在tableView(_:cellForRowAt:)方法中实现它有很多if let ...

防止这种情况的一种方法是使用泛型类型。泛型编程是避免样板代码的绝佳方式,有助于在编译期间定义错误。

通用代码使您能够编写灵活的、可重用的函数和类型,这些函数和类型可以与任何类型一起使用,具体取决于您定义的要求。您可以编写避免重复并以清晰、抽象的方式表达其意图的代码。 苹果文档

让我们制定每个单元应遵守的第一个协议。

protocol ConfigurableCell {
    associatedtype DataType
    func configure(data: DataType)
}

//example of UserCell
class UserCell: UITableViewCell, ConfigurableCell {
    @IBOutlet weak var avatarView: UIImageView!
    @IBOutlet weak var userNameLabel: UILabel!

    func configure(data user: User) {
        avatarView.image = UIImage(named: user.imageName)
        userNameLabel.text = user.name
    }
}

现在我们可以创建一个通用的单元格配置器来配置我们的表格单元格。

protocol CellConfigurator {
    static var reuseId: String { get }
    func configure(cell: UIView)
}

class TableCellConfigurator<CellType: ConfigurableCell, DataType>: CellConfigurator where CellType.DataType == DataType, CellType: UITableViewCell {
    static var reuseId: String { return String(describing: CellType.self) }

    let item: DataType

    init(item: DataType) {
        self.item = item
    }

    func configure(cell: UIView) {
        (cell as! CellType).configure(data: item)
    }
}

现在我们需要对 ViewModel 进行一些调整:

typealias UserCellConfigurator = TableCellConfigurator<UserCell, User>
typealias MessageCellConfigurator = TableCellConfigurator<MessageCell, String>
typealias ImageCellConfigurator = TableCellConfigurator<ImageCell, URL>

class TableViewModel {
    let items: [CellConfigurator] = [
        UserCellConfigurator(item: User(name: "John Smith", imageName: "user3")),
        MessageCellConfigurator(item: "Hi, this is a message text. Tra la la. Tra la la."),
        ImageCellConfigurator(item: Bundle.main.url(forResource: "beach@2x", withExtension: "jpg")!),
        UserCellConfigurator(item: User(name: "Jessica Wood", imageName: "user2")),
        MessageCellConfigurator(item: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."),
    ]
}

而已!

您可以轻松添加新单元格,而无需编辑 ViewController 的代码。

让我们在表格视图中添加一个WarningCell。

1.符合ConfigurableCell协议。2.在 ViewModel 的类中为该单元添加 TableCellConfigurator。

class WarningCell: UITableViewCell, ConfigurableCell {
    @IBOutlet weak var messageLabel: UILabel!

    func configure(data message: String) {
        messageLabel.text = message
    }
}

//cell configurator for WarningCell
TableCellConfigurator<WarningCell, String>(item: "This is a serious warning!")

有关更多信息,请点击此链接


推荐阅读