在 SwiftUI 中,List
会自动格式化您传递给它的 subview ,所以像这样:
List {
Text("A")
Text("B")
}
将导致正确放置两个 TextView ,它们之间有分隔符等...
此外,还可以混合静态和动态生成的数据,如下所示:
List {
Text("A")
ForEach(foo) { fooElement in CustomView(fooElement) }
Text("B")
ForEach(bar) { barElement in CustomView(barElement) }
}
我的目标是编写我自己的自定义类型,允许其用户使用这种类型(即:允许用户使用新的函数构建器 DSL 提供 View 的 View ,而不需要他们编写自己的修饰符来放置它们在屏幕上),但我完全不知道在我的自定义 View 的初始化程序中放入什么。
原生 SwiftUI View 正在使用 @ViewBuilder
并接收符合 View
的通用类型,但它们能够提取元素的方式(例如, ForEach
View )很神秘,我敢肯定这根本不可能。
也许我漏掉了什么,所以我很想知道您对此有何看法?
编辑:
举个例子可能更清楚。你们中的许多人一定已经在网上看到了很好的示例,这些示例使用 ZSack
将卡片排列在彼此之上,那么如果我想创建一个 CardStack
类型来允许我的用户会写这样的代码吗?
CardStack {
SomeView()
AnotherView()
ForEach(1...10) { i in
NumberView(i)
}
}
这将导致 12 张卡片相互堆叠,请注意类型不是同质的并且我们使用了 ForEach
。
最佳答案
挑战的第一部分是弄清楚 ViewBuilder。至于从 child 到祖先的数据流向,请继续阅读:
通过使用偏好(查看我的文章 https://swiftui-lab.com/communicating-with-the-view-tree-part-1/),您可以让信息从 child 流向祖先。
一旦您完全了解首选项并让它们发挥作用,您就可以尝试使实现更加简洁和透明,这样 Stack 的最终用户甚至都不会意识到他正在使用首选项。为此,您可以使用一些 View 扩展。最终代码可能如下所示:
CardStack {
SomeView().card(title: "", border: .red)
AnotherView().card(title: "", border: .green)
ForEach(items) { item in
NumberView(item).card(item.name, border: .blue)
}
}
你的 .card()
实现可能是这样的:
extension View {
func card(title: String, border: Color) -> CardCustomizer<Self> {
return CardCustomizer(viewContent: self, title: title, border: border)
}
}
struct CardCustomizer<Content: View>: View {
public let viewContent: Content
public let title: String
public let border: Color
var body: some View {
viewContent.preference(MyCardPref.self, value: ...)
}
}
我认为最好先尝试在没有 View 扩展的情况下让它工作,这样你就做对了一件事情。然后继续使用扩展封装首选项逻辑。不然要处理的新东西太多了。
关于swift - 自定义 "list" View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57010729/