Индивидуальное изменение дочерних представлений, передаваемых в контейнер с помощью @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)
}
}
}
Выход-: