在 SwiftUI 中使用 @ViewBuilder 单独修改传递给容器的子视图

问题描述

在 SwiftUI 中,您可以将多个视图作为一个参数传递给另一个视图,这要归功于 @viewbuilder。每个子视图都被单独处理,因此在传递之后,可以包含在 vstackHStack 等中。

我的问题是:这个容器视图是否可以单独修改传递给它的每个子视图?例如,通过在每个子视图之间添加一个 Spacer

这是我正在寻找的等效功能

struct SomeContainerView<Content: View> {
    
    init(@viewbuilder content: @escaping () -> Content) {
        self.content = content()
    }
    
    let content: Content
    
    var body: some View {
        HStack {
            ForEach(content.childViews,id: \.id) { child in
                // Give each child view a background
                // Add a Spacer in between each child view
                // Conditional formatting
                // Etc.
            }
        }
    }
    
}

我相信这是可能的,因为标准的 SwiftUI 容器视图可以影响传递给它们的子视图的外观和布局。除非这是通过 SwiftUI 的内部功能实现的?

解决这个问题的一种方法可能是传入多个视图参数而不是一个,尽管如果需要传递多个视图,这是不灵活和混乱的。一组视图似乎也没有意义,因为我在 SwiftUI 中的任何地方都没有注意到这种模式。

解决方法

根据我使用 @ViewBuilder 的个人经验,这是不可能的,当您将例如 Button 和 Text 以及 HStack 之类的传递给 SomeContainerView 时,它会将所有这些作为单个视图,如 VStack 或任何您命名的视图。并且任何修改都会发生在整个视图上,但是当您将 ForEach 传递给 SomeContainerView 时,事情会变得不同,在这种情况下,在发送 SomeContainerView 之前,您或多或少具有相同级别的控制,并且您可以根据需要修改每一行,但是它是有限的控制,例如,您无法再访问可以在 ForEach 中访问的索引或项目。 让我给你更多的光来看东西,即使你在 body 中工作我的意思是在 body 中运行一行代码后更高级别的观察和控制视图> 它不再是可编辑的,例如你有一个 Text 并且你给这个 Text 一个 backgroundColor,过了一段时间你想摆脱那个 Color,所以 SwiftUI 在给那个 bachgroundColor 后不再有控制权,那么它必须重新渲染/重新构建没有 backgroundColor 的文本来回答你的命令,这是 SwiftUI 和 UIKit-Swift 之间的巨大差异,一个视图就像一颗子弹,你无法抓住它或编辑它,你必须发射一个新的子弹到你想要的目标,所以再次回到你的问题,在你发送一个或一些视图到你的 SomeContainerView 之后,它几乎把它当作一个单一的视图。

但是如果你真的很想实现你想要的事情,那么你应该尝试其他的事情,比如一个 AnyView 数组可以解决你的问题,但你必须密切关注管理和控制这个数组。

,

据我所知,你不能做这样的事情。但是您可以使用数组 View 并遍历该视图并修改您的子视图。

这是演示:

struct SomeModel<Content: View>: Identifiable {
    var body: Content
    var id = UUID()
}

public struct SomeContainerView<Content: View>: View {
    
    let items: [SomeModel<Content>]
    
    init(items: [SomeModel<Content>]) {
        self.items = items
    }
    
    public var body: some View {
        HStack {
            ForEach(items.indices) { index in
                items[index].body.background(index % 2 == 0 ? Color.red : Color.green)
            }
        }
    }
}

struct TestSomeView: View {
    
    var body: some View {
        SomeContainerView(items: [.init(body: Text("Child_view_1")),.init(body: Text("Child_view_2")),.init(body: Text("Child_view_3"))])
    }
}

enter image description here

,

一个更好的主意是让调用者决定如何在视图中布局内容,然后模型决定每个输入的数据。检查下面的代码-:

import SwiftUI

struct SomeContainerView<Content: View>:View {
    
    var model:[Model] = []
    
    init(model:[Model],@ViewBuilder content: @escaping (Model) -> Content) {
        self.content = content
        self.model = model
    }
    
    let content: (Model) -> Content
    
    var body: some View {
        
        VStack{
            ForEach(model,id:\.id,content: content)
            Spacer()
        }
    }
}

struct MainView:View {
    @ObservedObject var modelData:objects
    
    var body: some View{
        SomeContainerView(model: modelData.myObj){ data in
            Text(data.name)
                .background(data.color)
            
        }
    }
}

struct Model{
    
    var id = UUID().uuidString
    var name:String
    var color:Color
    
    init(name:String,color:Color) {
        self.name = name
        self.color = color
    }
    
}

class objects:ObservableObject,Identifiable{
    
    var id = UUID().uuidString
    @Published var myObj:[Model] = []
    
    init() {
        initModel()
    }
    
    func initModel(){
        let model = Model(name: "Jack",color: .green)
        let model1 = Model(name: "hey Jack",color: .red)
        
        myObj.append(model)
        myObj.append(model1)
    }
    
}

@main -:

import SwiftUI

    @main
    struct WaveViewApp: App {
        
        @StateObject var dataObj : objects
        
        init() {
            let obj = objects()
           _dataObj = StateObject(wrappedValue: obj)
            
        }
        var body: some Scene {
            WindowGroup {
                MainView(modelData: dataObj)
            }
        }
    }

输出-:

enter image description here