如何在SwiftUI中循环遍历ViewBuilder内容子视图

问题描述

因此,我试图创建一个包含viewbuilder内容的视图,循环浏览该内容的视图,并在每个视图与另一个视图之间添加分隔线

struct BoxWithdividerView<Content: View>: View {
    let content: () -> Content
    init(@viewbuilder content: @escaping () -> Content) {
        self.content = content
    }
    var body: some View {
        vstack(alignment: .center,spacing: 0) {
            // here
            
        }
        .background(Color.black)
        .cornerRadius(14)
    }
}

所以,在我写“ here”的地方,我想遍历内容的视图,如果这是有道理的。我将编写无效的代码,但该代码说明了我要实现的目标:

ForEach(content.subviews) { view  in
     view
     Divider()
}

该怎么做?

解决方法

所以我最终这样做了

@_functionBuilder
struct UIViewFunctionBuilder {
    static func buildBlock<V: View>(_ view: V) -> some View {
        return view
    }
    static func buildBlock<A: View,B: View>(
        _ viewA: A,_ viewB: B
    ) -> some View {
        return TupleView((viewA,Divider(),viewB))
}
}

然后我像这样使用函数构建器

struct BoxWithDividerView<Content: View>: View {
    let content: () -> Content
    init(@UIViewFunctionBuilder content: @escaping () -> Content) {
        self.content = content
    }
    var body: some View {
        VStack(spacing: 0.0) {
            content()
        }
        .background(Color(UIColor.AdUp.carbonGrey))
        .cornerRadius(14)
    }
}

但是问题是这仅适用于最多2个表达式视图。我将发布一个单独的问题,关于如何能够将其传递给数组

,

我刚刚回答了另一个类似的问题,link here。将对链接的答案进行任何改进,因此请先检查那里。

然而,这里的答案具有相同的 TupleView 扩展名,但视图代码不同。

用法:

struct ContentView: View {
    
    var body: some View {
        BoxWithDividerView {
            Text("Something 1")
            Text("Something 2")
            Text("Something 3")
            Image(systemName: "circle")  // Different view types work!
        }
    }
}

您的BoxWithDividerView

struct BoxWithDividerView: View {
    let content: [AnyView]
    
    init<Views>(@ViewBuilder content: @escaping () -> TupleView<Views>) {
        self.content = content().getViews
    }
    var body: some View {
        VStack(alignment: .center,spacing: 0) {
            ForEach(content.indices) { index in
                if index != 0 {
                    Divider()
                }
                
                content[index]
            }
        }
//        .background(Color.black)
        .cornerRadius(14)
    }
}

最后是最重要的,TupleView 扩展名:

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