在表视图中显示异构数据

移动应用程序的常见发展之一是使用表格显示信息 。 当您具有同类实体的集合时,可以轻松完成此任务,但是当此集合具有n个不同的实体时,它将变得更加棘手。 在本文中,我将展示一个示例,说明如何使用适配器模式在表视图中显示异构数据 。 公开的示例是具有两种消息类型的消息传递应用程序。 文字和图像。

当我们面临在表中显示异构数据集合的问题时,直接的解决方案是在“ cellForRowAtIndexPath”方法中使用模式匹配来确定我们需要使用哪个单元格来显示集合中的当前实体。

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
let currentItem = itemsArray[indexPath.row]
switch currentItem {
case let item as TextMessage:<
let cell = tableView.dequeueReusableCell(withIdentifier: "TextMessageCell", for: indexPath) as! TextMessageCell<
cell.configure(viewModel: item)
return cell
case let item as ImageMessage:
let cell = tableView.dequeueReusableCell(withIdentifier: "ImageMessageCell", for: indexPath) as! ImageMessageCell
cell.configure(viewModel: item)
return cell
default:
fatalError("unsupported cell for type: \(currentItem)")
}
}

此解决方案有几个缺点:

  • 它不是可伸缩的,这意味着每当我们需要显示一种新类型的实体时,切换案例都会增加。
  • 我们正在重复对方法configure(viewModel :)的调用,该方法可以是单元接口的一部分。
  • 由于我们没有严格类型化的实体,因此我们需要为开关提供默认情况。

适配器图案

适配器模式用于将一个接口转换为我们的系统将使用的另一个接口。

让我们看一下使用适配器模式的解决方案,以使其具有可伸缩性并避免重复代码。

该解决方案将包含两个组件:

  • 适配器接口
  • 适配器收集架(适配器图)

首先我们需要创建一个适配器接口,该接口将具有基本的单元配置属性和配置。

 protocol PostTableViewAnyCellAdapter { 
var reuseIdentifier: String { get }
var preferredHeight: CGFloat { get }
func configure(cell: UITableViewCell, with viewModel: Post) }

该接口的实现将同时接收模型和单元的通用类型。

 class PostCellAdapter: PostTableViewAnyCellAdapter where CellType.ViewModelType == ViewModelType { 
var reuseIdentifier: String {
return CellType.reuseIdentifier
}
var preferredHeight: CGFloat {
return CellType.preferredHeight
}
init(tableView: UITableView) {
register(into: tableView)
}
func register(into tableView: UITableView) {
switch CellType.registerMethod {
case .nib(let nib):
tableView.register(nib, forCellReuseIdentifier: CellType.reuseIdentifier)
case .classReference(let cellClass):
tableView.register(cellClass, forCellReuseIdentifier: CellType.reuseIdentifier)
}
}
func configure(cell: UITableViewCell, with viewModel: Post) {
let typedCell = cell as! CellType
let typedViewModel = viewModel as! ViewModelType
typedCell.configure(viewModel: typedViewModel)
}
}

然后,我们创建一个类,该类将保存此适配器接口的集合。 此类称为适配器映射。

 struct PostsAdapterMap { 
typealias InternalMapType = [String: PostTableViewAnyCellAdapter]
private var internalMap = InternalMapType()
  subscript(viewModel: Post) -> PostTableViewAnyCellAdapter { 
let key = String(describing: type(of: viewModel))
return internalMap[key]!
}
mutating func add(adapter: PostCellAdapter) {
let key = String(describing: ViewModelType.self)
internalMap[key] = adapter
}
}

现在我们已经拥有了所需的所有组件,我们可以通过简单地创建该Adapter映射类的新实例,在视图控制器中使用它。

 var tableAdapter = PostsAdapterMap() 

要将单元格适配器注册到表视图,我们使用一对UITableviewCell子类和一个模型创建Adapter的实例:

 let textAdapter = PostCellAdapter(tableView: tableView) tableAdapter.add(adapter: textAdapter) 

最后,我们对方法“ cellForRowAtIndexPath”的新实现如下所示:

 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
let currentItem = itemsArray[indexPath.row]
let currentAdapter = tableAdapter[currentItem]
let cell = tableView.dequeueReusableCell(withIdentifier: currentAdapter.reuseIdentifier, for: indexPath)
currentAdapter.configure(cell: cell, with: currentItem)
return cell
}

我们不再使用模式匹配直接配置单元格视图类,而是使用适配器接口来接收模型和回收的单元格视图。 适配器映射将使用给定模型的适当方法来配置当前单元

这如何解决可扩展性? 如果我们需要在此聊天应用程序中显示另一种消息(例如,视频消息),则只需使用其configure方法创建单元格视图,定义模型并将其添加到适配器映射即可。 适配器映射将处理所有内容,因此,我们的表视图数据源方法将保持不变。

移动应用程序移动开发