swift - 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
}
无论如何我可以归档这个目标吗?
解决方案
想象一下你的数据会是这样的:
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!")
有关更多信息,请点击此链接
推荐阅读
- sql-server - SQL Server 不使用特定索引
- api - 如何让 Fluentd HTTP 插件在响应正文中返回数据
- python - Flask/Jinja2 - 包含相对于当前模板的模板
- bash - docker-compose.yml 传递 arg 以从文件内容构建
- neural-network - GAN 中模式丢弃和模式崩溃之间的区别?
- c++ - 整数序列实现 C++
- java - 如何将 60 个整数存储在要从数据库写入/读取的类中?
- ios - Google Maps iOS Location 位于 MapView 的一角
- javascript - 使用ajax将js变量发送到php文件
- kotlin - 为可以包含自身实例的类自动生成适配器时,项目无法编译