Напишите свой контейнер-представление, принимающий @ViewBuilder-замыкание
Напишите переиспользуемый контейнер CardView, оборачивающий любое переданное вызывающим содержимое в стилизованную карточку (отступы, фон, скругления).
Требования:
- Вызывающий передаёт детей хвостовым замыканием, напр.
CardView { Text("Hi"); Image(...) }. - Принимайте несколько дочерних представлений без массива и без
AnyView.
struct CardView<Content: View>: View {
// сохраните содержимое и добавьте @ViewBuilder-инициализатор
var body: some View {
// оберните содержимое в стилизованный контейнер
}
}
Допишите реализацию.
Сделайте обобщённую struct, хранящую content: Content (Content: View), и пометьте замыкание инициализатора @ViewBuilder content: () -> Content. Сохраните результат и разместите content в раскладке. Атрибут даёт передать несколько представлений замыканием.
- ✗Объявляют замыкание как
[AnyView]вместо обобщённого@ViewBuilder-замыкания - ✗Забывают
@ViewBuilder, из-за чего компилируется лишь одно дочернее представление - ✗Делают замыкание содержимого
@escapingи вызывают его заново при каждом рендере
- →Почему
@ViewBuilderпозволяет вызывающему передать несколько представлений без запятых и массива? - →Какой конкретный тип производит билдер для нескольких детей?
A generic content parameter plus a @ViewBuilder initializer is all it takes:
struct CardView<Content: View>: View {
private let content: Content
init(@ViewBuilder content: () -> Content) {
self.content = content() // build once, store the result
}
var body: some View {
VStack(alignment: .leading, spacing: 12) {
content
}
.padding()
.background(.regularMaterial)
.clipShape(RoundedRectangle(cornerRadius: 16))
}
}
// Usage — several children, one trailing closure, no array, no AnyView:
CardView {
Text("Title").font(.headline)
Text("Subtitle").foregroundStyle(.secondary)
}
@ViewBuilder on the initializer parameter is what turns the { ... } block of several views into a single Content value (a TupleView for multiple children). The closure is called once in init and its result stored, so content is a plain child of the layout — no AnyView, no per-render rebuild.