Как перебирать подвиды контента вьюбилдера в SwiftUI
Итак, я пытаюсь создать представление, которое принимает содержимое 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)
}
}
так что там, где я написал "здесь", я хочу перебрать просмотры контента, если это имеет смысл. Я напишу код, который не работает, но объясняет, чего я пытаюсь достичь:
ForEach(content.subviews) { view in
view
Divider()
}
Как это сделать?
3 ответа
Я только что ответил на другой похожий вопрос, ссылка здесь . Любые улучшения будут внесены в связанный ответ, поэтому сначала проверьте его.
Однако вот ответ с тем же расширением, но с другим кодом представления.
Применение:
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)
}
}
Результат:
Итак, я закончил этим
@_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)
}
}
Но проблема в том, что это работает только для двух представлений выражений. Я собираюсь отправить отдельный вопрос о том, как передать ему массив
Расширение ответа Эдди с использованием Swift 5.7 и buildPartialBlock из построителей результатов:
@resultBuilder
struct MyViewBuilder {
static func buildPartialBlock<C: View>(first: C) -> TupleView<(C)> {
TupleView(first)
}
static func buildPartialBlock<C0, C1>(accumulated: C0, next: C1) -> TupleView<(C0, Divider, C1)> where C0: View, C1: View {
TupleView((accumulated, Divider(), next))
}
}
struct MyView<Content: View>: View {
@MyViewBuilder var content: Content
var body: some View {
VStack {
content
}
}
}
struct ContentView : View {
var body: some View {
MyView {
Text("Text1")
Text("Text2")
Image(systemName: "chart.bar.fill", variableValue: 0.3)
Text("Text3")
}
}
}
Однако у этого подхода есть одна загвоздка: он не позволяет нам рассматривать группу как «прозрачный контейнер», т. е. группа из двух элементов — это не то же самое, что два элемента, перечисленные без группы:
var body: some View {
MyView {
Text("Text1")
Text("Text2")
Group {
Image(systemName: "chart.bar.fill", variableValue: 0.3) // no Divider() between image and text
Text("Text3")
}
}
}
Если объединить 2 последних элемента в одну Группу, разделитель между ними исчезнет.
Единственный способ заставить его работать правильно с найденной мной группой описан здесь: https://movingparts.io/variadic-views-in-swiftui.