有什么方法可以在 SwiftUI 中使用 @ViewBuilder 创建/提取视图数组 [英] Is there any way to create/extract an array of Views using @ViewBuilder in SwiftUI

查看:27
本文介绍了有什么方法可以在 SwiftUI 中使用 @ViewBuilder 创建/提取视图数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个简单的 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆