如何设置我的 @resultBuilder 来与 ForEach 一起使用,但仍然遵守协议?

问题描述 投票:0回答:1

我在项目中定义了以下容器,第一个容器可以工作,但是第二个容器不能,因为它会引发以下错误。

参数类型“ForEach,Range.Element,PageX>”(又名“ForEach”)不符合预期类型“PageXItem”

@PageBuilder
在我的
NavX
结构上实现,如下所示。

@PageBuilder private var content: () -> Content

我希望能够使用独立的

PageX
或通过使用
ForEach
来使用动态集合。无论它是
ForEach(array.indices)
还是任何序列都没关系,这样
ForEach
的每个元素都保证是
PageX

NavX {
   PageX {
   }
}

NavX {
   ForEach(...) { _ in 
      PageX {...}
   }
}

这是目前使用的

@PageBuilder
。最后两个是我最近为实现此目的所做的尝试,但没有一个被证明是成功的。

@resultBuilder
public struct PageBuilder {

    /// Builds a collection of `PageXItem` views.
    ///
    /// - Parameter components: A variadic list of views conforming to `PageXItem`.
    /// - Returns: An array of views that conform to `PageXItem`.
    static public func buildBlock(_ components: any PageXItem...) -> [any PageXItem] {
        components
    }

    /// Builds a collection of `PageXItem` views, particularly useful for conditional view constructions.
    ///
    /// - Parameter components: A variadic list of arrays of views conforming to `PageXItem`.
    /// - Returns: An array of views that conform to `PageXItem`.
    static public func buildPartialBlock(_ components: [any PageXItem]...) -> [any PageXItem] {
        components.flatMap { $0 }
    }
    
    static public func buildFinalResult(_ component: [any PageXItem]) -> [any PageXItem] {
        component
    }
    
    static public func buildBlock<C: View>(_ component: ForEach<[any PageXItem], UUID, C>) -> ForEach<[any PageXItem], UUID, C> {
        component
    }
}
swift swiftui view foreach protocols
1个回答
0
投票

要与

ForEach
合作,您应遵守以下要求:

  1. 您的结构必须符合
    View
    (如果不符合),因为
    ForEach
    仅适用于视图。
struct Item {
  let name: String
}

extension Item: View {
  public var body: Never {
    fatalError()
  }
}
  1. 您的
    @resultBuilder
    必须仅使用专用协议运行。
protocol ItemConvertible {
  var items: [Item] { get }
}

extension Item: ItemConvertible {
  var items: [Item] {
    [self]
  }
}

@resultBuilder
struct ItemsBuilder {
  static func buildBlock(_ components: ItemConvertible...) -> [ItemConvertible] {
    components
  }
  
  static func buildFinalResult(_ components: [ItemConvertible]) -> [Item] {
    components.flatMap(\.items)
  }
}
  1. 您应该使用协议扩展
    ForEach
extension ForEach: ItemConvertible where Content == Item {
  var items: [Item] {
    data.map(content)
  }
}

现在您可以编写构建器函数了:

@ItemsBuilder
func content() -> [Item] {
  Item(name: "1")
  Item(name: "2")
  ForEach(3..<5) {
    Item(name: "\($0)")
  }
}

print(content()) // [Item(name: "1"), Item(name: "2"), Item(name: "3"), Item(name: "4")]
© www.soinside.com 2019 - 2024. All rights reserved.