Как установить относительную ширину в HStack, встроенном в ForEach в SwiftUI?

Я хотел создать список (без использования представления списка) атрибутов. Каждый атрибут представляет собой HStack, который содержит два текста, имя и значение. Я хочу, чтобы текст имени всегда составлял 30% ширины всего HStack, а текст значения использовал остальную часть горизонтального пространства. Высота каждого атрибута зависит от содержимого.

Я пытаюсь добиться этого, имея следующую точку зрения:

struct FatherList: View {
    let attributes: Attributes
    
    init(_ attributes: Attributes) {
        self.attributes = attributes
    }
    
    var body: some View {
        VStack(spacing: CGFloat.spacing.medium) {
            ForEach(
                attributes,
                id: \.name,
                content: ChildView.init
            )
        }
    }
}

который содержит следующий ChildView:

struct ChildView: View {
    let listItem: Product.Attribute
    
    init(_ attribute: Product.Attribute) {
        self.attribute = attribute
    }
    
    var body: some View {
        GeometryReader { geometry in
            HStack(alignment: .top, spacing: 0) {
                Text(attribute.name)
                    .bold()
                    .frame(width: 0.3 * geometry.size.width)
                    .background(Color.yellow)
                Text(attribute.value)
            }
            .fixedSize(horizontal: false, vertical: true)
            .background(Color.red)
        }
    }
}

И вот результат:

введите описание изображения здесь

Дочерние представления перекрываются, чего я не хочу, я хочу, чтобы дочерние представления расширялись и следовали друг за другом. Я использую geometryReader для достижения относительной ширины, описанной выше. Что я делаю неправильно?


person Danny Richard Aboo    schedule 20.07.2020    source источник


Ответы (1)


Вот демонстрация возможного решения. Протестировано с Xcode 11.4 / iOS 13.4

демо

Примечание. ViewHeightKey взято из еще одного моего решения

struct ChildView: View {
    let attribute: Attribute

    @State private var fitHeight = CGFloat.zero

    var body: some View {
        GeometryReader { geometry in
            HStack(alignment: .top, spacing: 0) {
                Text(self.attribute.name)
                    .bold()
                    .frame(width: 0.3 * geometry.size.width, alignment: .leading)
                    .background(Color.yellow)
                Text(self.attribute.value)
                    .fixedSize(horizontal: false, vertical: true)
                    .frame(width: 0.7 * geometry.size.width, alignment: .leading)
            }
            .background(Color.red)
            .background(GeometryReader {
                Color.clear.preference(key: ViewHeightKey.self,
                    value: $0.frame(in: .local).size.height) })
        }
        .onPreferenceChange(ViewHeightKey.self) { self.fitHeight = $0 }
        .frame(height: fitHeight)
    }
}
person Asperi    schedule 20.07.2020
comment
Спасибо! Это решило проблему! Мне также пришлось добавить .fixedSize(horizontal: false, vertical: true) к тексту (self.attribute.name), так как я хотел также изменить размер по вертикали в соответствии с содержимым имени. - person Danny Richard Aboo; 21.07.2020