如何在ForEach中转换不同的SwiftUI视图结构来显示?



我想根据分配给项目的内容显示不同类型的视图。每个相关视图都遵循此协议:

public protocol ItemDisplayer {
init(item: ItemObject)
}

该结构体存储Item对象及其关联的外观视图对象。

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?
}
}
}

如果itemdisplay直接强制转换为DefaultTimerView或OtherItemView,则强制转换工作。但是我想转换为任意视图类型

编辑:我在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
}

或者在Cocoa中:

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())
}

最新更新