Индивидуальное изменение дочерних представлений, передаваемых в контейнер с помощью @ViewBuilder в SwiftUI

В SwiftUI вы можете передавать несколько представлений в качестве одного параметра другому представлению благодаря @ViewBuilder

. Каждое дочернее представление обрабатывается индивидуально, поэтому после передачи может содержаться в VStack, HStack и Т. Д.

Мой вопрос: возможно ли для этого представления контейнера индивидуально изменять каждое переданное ему дочернее представление? Например, добавив 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 не заметил этого шаблона.

3 ответа

Насколько мне известно, вы не можете сделать что-то подобное. Но вы можете использовать массив 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"))])
    }
}

из моего личного опыта работы с @ViewBuilder это невозможно, когда вы передаете, например, Button и Text и, возможно, HStack в SomeContainerView, он принимает все это как единое представление, например VStack или как вы его называете. и любая модификация происходит со всем представлением, но все становится по-разному, когда вы передаете ForEach своему SomeContainerView, в этом случае более или менее у вас есть тот же уровень контроля перед его отправкой SomeContainerView, и вы можете изменять каждую отдельную строку по своему усмотрению, но это ограниченный контроль, например, у вас больше нет доступа к индексу или элементу, который вы могли бы иметь в ForEach. Позвольте мне дать вам больше света, чтобы увидеть вещи, даже если вы работаете в теле, я имею в виду более высокий уровень наблюдения и управления View, после запуска кода одной строки в теле он больше не редактируется.Например, у вас есть Text, и вы дали backgroundColor этому Text, и через некоторое время вы хотите избавиться от этого Color, поэтому SwiftUI больше не имеет контроля после того, как предоставил этот bachgroundColor, тогда он должен быть повторно -render/re-build the Text без backgroundColor, чтобы ответить на ваш заказ, и это большая разница между SwiftUI и UIKit-Swift, View похож на пулю после пожара, вы не можете его поймать или отредактировать, вам нужно запустить новый bullet к цели, которую вы хотите, поэтому, возвращаясь к вашему вопросу снова, после того, как вы отправите или несколько View в свой SomeContainerView, он будет воспринимать его почти как единый View.

Но если вы действительно плохо хотите, чтобы то, что вы хотите, произошло, тогда вам следует попробовать другие вещи, например, массив AnyView, который решит вашу проблему, но вы должны следить за управлением и контролем этого массива.

Лучше было бы позволить вызывающей стороне решать, как размещать контент в представлении, а модели определять данные для каждого ввода. Проверьте код ниже:

      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)
    }
    
}

@главный -:

      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)
            }
        }
    }

Выход-:

Другие вопросы по тегам