有什么方法可以在 SwiftUI 中使用 @ViewBuilder 创建/提取视图数组 [英] Is there any way to create/extract an array of Views using @ViewBuilder in SwiftUI
问题描述
我正在尝试创建一个简单的 struct
,它接受一个视图数组并返回一个普通的 VStack
,其中包含这些视图,但它们都是对角堆叠的.
代码:
struct LeaningTower: View {var 视图:[内容]var主体:一些视图{虚拟堆栈{ForEach(0..<views.count) { index inself.views[索引].offset(x: CGFloat(index * 30))}}}}
现在这很好用,但每当我不得不调用它时我总是很生气:
LeaningTower(views: [Text("Something 1"), Text("Something 2"), Text("Something 3")])
在这样的数组中列出视图对我来说似乎非常奇怪,所以我想知道是否有一种方法可以使用 @ViewBuilder
来调用我的 LeaningTower
结构,例如这个:
LeaningTower {//与创建常规 VStack 的方式相同文本(某事 1")文本(东西 2")文本(东西 3")//然后将所有这些文本放在我的视图"数组中}
如果有办法使用 @ViewBuilder
来创建/提取视图数组,请告诉我.
(即使不可能使用 @ViewBuilder
任何能让它看起来更整洁的东西都会有很大帮助)
很少需要从数组中提取视图.如果您只是想将 @ViewBuilder
内容传递到视图中,您只需执行以下操作:
struct ContentView:查看{var主体:一些视图{VStackReplica {文本(第一个")文本(第二个")文本(第三个")}}}struct VStackReplica<内容:视图>:视图{@ViewBuilder 让内容:() ->内容var主体:一些视图{VStack(内容:内容)}}
如果这对您的用例来说还不够,请参见下文.
我有一个通用版本可以工作,所以不需要为不同长度的元组制作多个初始值设定项.此外,视图可以是您想要的任何内容(您不限制每个 View
为相同类型).
您可以在
I'm trying to create a simple struct
that accepts an array of Views and returns an ordinary VStack
containing those Views except they all are stacked diagonally.
Code:
struct LeaningTower<Content: View>: View {
var views: [Content]
var body: some View {
VStack {
ForEach(0..<views.count) { index in
self.views[index]
.offset(x: CGFloat(index * 30))
}
}
}
}
Now this works great but I always get annoyed whenever I have to call it:
LeaningTower(views: [Text("Something 1"), Text("Something 2"), Text("Something 3")])
Listing the views in an array like that seems extremely odd to me so I was wondering if there was a way I could use @ViewBuilder
to call my LeaningTower
struct like this:
LeaningTower { // Same way how you would create a regular VStack
Text("Something 1")
Text("Something 2")
Text("Something 3")
// And then have all of these Text's in my 'views' array
}
If there's a way to use @ViewBuilder
to create/extract an array of Views please let me know.
(Even if it isn't possible using @ViewBuilder
literally anything that will make it look neater will help out a lot)
It's rare that you need to extract views from an array. If you are just looking to pass @ViewBuilder
content into a view, you can simply do the following:
struct ContentView: View {
var body: some View {
VStackReplica {
Text("1st")
Text("2nd")
Text("3rd")
}
}
}
struct VStackReplica<Content: View>: View {
@ViewBuilder let content: () -> Content
var body: some View {
VStack(content: content)
}
}
If this isn't sufficient for your use-case, then see below.
I have got a generic version working, so there is no need to make multiple initializers for different lengths of tuples. In addition, the views can be anything you want (you are not restricted for every View
to be the same type).
You can find a Swift Package I made for this at GeorgeElsham/ViewExtractor. That contains more than what's in this answer, because this answer is just a simplified & basic version. Since the code is slightly different to this answer, so read the README.md
first for an example.
Back to the answer, example usage:
struct ContentView: View {
var body: some View {
LeaningTower {
Text("Something 1")
Text("Something 2")
Text("Something 3")
Image(systemName: "circle")
}
}
}
Definition of your view:
struct LeaningTower: View {
private let views: [AnyView]
init<Views>(@ViewBuilder content: @escaping () -> TupleView<Views>) {
views = content().getViews
}
var body: some View {
VStack {
ForEach(views.indices) { index in
views[index]
.offset(x: CGFloat(index * 30))
}
}
}
}
TupleView
extension (AKA where all the magic happens):
extension TupleView {
var getViews: [AnyView] {
makeArray(from: value)
}
private struct GenericView {
let body: Any
var anyView: AnyView? {
AnyView(_fromValue: body)
}
}
private func makeArray<Tuple>(from tuple: Tuple) -> [AnyView] {
func convert(child: Mirror.Child) -> AnyView? {
withUnsafeBytes(of: child.value) { ptr -> AnyView? in
let binded = ptr.bindMemory(to: GenericView.self)
return binded.first?.anyView
}
}
let tupleMirror = Mirror(reflecting: tuple)
return tupleMirror.children.compactMap(convert)
}
}
Result:
这篇关于有什么方法可以在 SwiftUI 中使用 @ViewBuilder 创建/提取视图数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!