我有两种协议,一种用于ViewModel,一种用于将ViewModel类型作为关联类型的ConfigurableView。
public protocol ViewModel {}
public protocol ConfigurableView {
associatedtype ViewModelType: ViewModel
func configure(with viewModel: ViewModelType)
}
在我的使用抽象模型配置抽象视图的方法中:
let viewModel = getMyViewModel() // returns ViewModel
if let configurableView = cell as? ConfigurableView {
configurableView.configure(with: viewModel)
}
我得到“协议'ConfigurableView'只能用作通用约束,因为它具有Self或关联的类型要求”。
如果它是ConfigurableView实例,如何告诉编译器我要使用该实例具有的任何关联类型来配置视图?
答案 0 :(得分:1)
实际上,我发现我认为是一个不错的解决方案,不需要过多地修改我的体系结构。感谢@lib使我走上正确的道路。诀窍是要在上面的协议中没有对associatedType的要求,并且要有一个将通用ViewModel强制转换为特定类型的associatedType的扩展。相信这是类型擦除吗?但这看起来与我阅读的任何示例都不一样。
public protocol ViewModel {}
/*
This parent protocol exists so callers can call configure on
a ConfigurableView they don't know the specific type of.
*/
public protocol AnyConfigurableView {
func configure(with anyViewModel: ViewModel)
}
public protocol ConfigurableView: AnyConfigurableView {
associatedtype ViewModelType: ViewModel
func configure(with viewModel: ViewModelType)
}
/*
This extension does the trick of converting from the generic
form of ConfigurableView to the specific form.
*/
public extension ConfigurableView {
func configure(with anyViewModel: ViewModel) {
guard let viewModel = anyViewModel as? ViewModelType else {
return
}
configure(with: viewModel)
}
}
用法:
let viewModel = getViewModel()
(someView as? AnyConfigurableView)?.configure(with: viewModel)
答案 1 :(得分:0)
除了类型约束,您不能以其他方式使用通用协议。如果没有定义泛型类型,编译器将无法比较类型一致性。如果我对您的理解正确,则需要定义通用的CellConfigurator
类。以下是可能的解决方案之一:
protocol ConfigurableCell {
associatedtype DataType
func configure(viewModel: DataType?)
}
protocol CollectionViewCellConfigurator {
static var reuseId: String { get }
func configure(cell: UICollectionViewCell)
var item: UniqueIdentifiable? { get }
}
final class CellConfigurator<CellType: ConfigurableCell, DataType>: CollectionViewCellConfigurator where CellType.DataType == DataType, CellType: UICollectionViewCell {
/// Cell Reuse identifier
static var reuseId: String { return CellType.reuseId }
/// Configures cell and populates it with `viewModel`
///
/// - Parameter cell: Cell to configure
func configure(cell: UICollectionViewCell) {
(cell as! CellType).configure(viewModel: item as? DataType)
}
/// Initializer
///
/// - Parameter item: Data item (usually ViewModel of the cell)
init(item: DataType?) {
self.item = item
}
}
您的数据源现在将以CellConfigurators
的形式运行CellConfigurator<CellType /*UI(CollectionView/TableView)Cell subclass*/, CellData /*Data you need to populate to the cell*/>(item: cellData)
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let configItem = yourDataSource.rows[indexPath.row]
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: type(of: configItem).reuseId, for: indexPath)
configItem.configure(cell: cell)
return cell
}
希望有帮助。祝你好运