swiftui - 如何在 ForEach 中转换不同的 SwiftUI View 结构来显示?
问题描述
我想根据分配给项目的内容显示不同类型的视图。每个相关视图都符合此协议:
public protocol ItemDisplayer {
init(item: ItemObject)
}
此结构存储 Item 对象及其关联的外观 View 对象。
public struct ItemWithDisplay {
private let item: ItemObject
private let itemDisplayer : ItemDisplayer
public init(item : ItemObject, displayer : ItemDisplayer) {
self.item = item
self.itemDisplayer = displayer
}
public func getDisplayer() -> TimeCounterDisplayer {
return itemDisplayer
}
public func getItem() -> ItemObject {
return item
}
}
有几种视图以某种方式显示数据,取决于分配给 ItemObject 的内容。
import SwiftUI
struct OtherItemView: View, ItemDisplayer {
private var item: ItemObject
init(item: ItemObject) { self.item = item }
var body: some View { ... }
}
struct DefaultItemView: View, ItemDisplayer {
private var item: ItemObject
init(item: ItemObject) { self.item = item }
var body: some View { ... }
}
// other ItemViews ...
我想在 modelView 类中将项目与视图分开,如下所示:
switch displayerType {
case .other:
self.itemWithDisplayArray.append(ItemWithDisplay(item: item, displayer: OtherItemView(itemToDisplay: item)))
case .another:
self.itemWithDisplayArray.append(ItemWithDisplay(item: item, displayer: AnotherItemView(itemToDisplay: item)))
default:
self.itemWithDisplayArray.append(ItemWithDisplay(item: item, displayer: DefaultItemView(itemToDisplay: item)))
}
这是以下问题所需的信息。我的问题涉及以下部分:
ScrollView {
ForEach(viewModel.itemWithDisplayArray, id: \.itemId) { item in
VStack {
//item.getDisplayer() as! DefaultItemView // It works
item.getDisplayer() as! AnyView // Cast from 'ItemDisplayer' to unrelated type 'AnyView' always fails // how could I fix this?
}
}
}
如果 ItemDisplayer 直接转换为 DefaultTimerView 或 OtherItemView ,则转换有效...但我想转换为任何 View 类型。
编辑:我在 UIKit 中做了这样的:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let itemViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "ItemViewCell", for: indexPath) as! ItemViewCell
let itemWithDisplay = itemWithDisplayArray[indexPath.row]
itemViewCell.containerView = UIView(frame: itemViewCell.contentView.frame)
itemViewCell.containerView!.addSubview(itemWithDisplay.getDisplayer() as! UIView) // <--the POINT is here: "as! UIView"
itemViewCell.contentView.addSubview(itemViewCell.containerView!)
itemWithDisplay.display()
return itemViewCell
}
或在可可中:
class ItemGridNSView : NSView {
private let itemWithDisplay : ItemWithDisplay
init(frame frameRect: NSRect, itemWithDisplay: ItemWithDisplay) {
self.itemWithDisplay = itemWithDisplay
super.init(frame:frameRect);
self.addSubview(itemWithDisplay.getDisplayer() as! NSView) // <-- Here too
itemWithDisplay.display()
}
...
我正在 SwiftUI 中寻找相同的解决方案。
我想保持领域层与视图逻辑的独立性,即任何视图特定信息(导入 SwiftUI)。我想在视图层中避免这种排序逻辑。
你能帮我解决这个问题吗?
提前感谢您提供的任何帮助!
解决方案
使ItemDisplayer
符合View
. 这样您就可以像普通的View
.
public protocol ItemDisplayer: View {
init(item: ItemObject)
}
然后进行更改ItemWithDisplay
以便您可以使用ItemDisplayer
(因为它现在具有相关的类型要求):
public struct ItemWithDisplay<Displayer: ItemDisplayer> {
private let item: ItemObject
private let itemDisplayer : Displayer
public init(item : ItemObject, displayer : Displayer) {
self.item = item
self.itemDisplayer = displayer
}
public func getDisplayer() -> Displayer {
return itemDisplayer
}
public func getItem() -> ItemObject {
return item
}
}
最后,您现在可以使用getDisplayer()
来获取视图,甚至无需将其转换为AnyView
:
VStack {
item.getDisplayer()
}
但是,要使某些东西成为AnyView
,您将执行以下操作:
VStack {
AnyView(item.getDisplayer())
}
推荐阅读
- javascript - Tensorflow.js 分词器
- javascript - Javascript - 在子块中重新定义变量 // 变量阴影
- python - Sklearn fit vs predict,列的顺序很重要吗?
- python - 异步语法错误
- java - 为什么我得到 char 数组的“非法表达式开始”和方法的“预期的类、接口或枚举”?
- linux - 在 makefile 中指定文件名
- javascript - 我想知道如何创建一个按钮,根据选择的单选按钮显示一个最终结果
- mysql - 键入“mysql -proot”后,MySQL 访问被拒绝
- c# - 使用 Mono.Cecil 将实例字段添加到类
- arrays - 为什么 System.Array 对象有 Add() 方法?