Макет SwiftUI с GeometryReader внутри ScrollView - PullRequest
1 голос
/ 03 февраля 2020

Я пытаюсь создать скроллируемую сетку предметов. Я создаю пользовательское представление с именем GridView, которое использует GeometryReader, чтобы разделить пространство на столбцы и строки в HStacks и VStacks. Но по какой-то причине его размер уменьшается почти до нуля, когда он внутри ScrollView. На скриншоте вы видите GridView (reddi sh) и его родительский VStack (greeni sh) сжался. Элементы внутри сетки все еще видны, отображаются за пределами ее области, но объекты не прокручиваются должным образом.

Почему размер GridView не является необходимым для содержания его элементов? Если бы это было так, я думаю, что этот интерфейс прокрутился бы правильно.

enter image description here

struct ContentView: View {
    var body: some View {
        ScrollView {
            VStack {
                Text("Section 1")
                GridView(columns: 2, items: [1, 3, 5, 7, 9, 11, 13, 15]) { num in
                    Text("Item \(num)")
                }
                .background(Color.red.opacity(0.2))
            }.background(Color.green.opacity(0.2))
        }.background(Color.blue.opacity(0.2))
    }
}

struct GridView<Content>: View where Content: View {
    var columns: Int
    let items: [Int]
    let content: (Int) -> Content

    init(columns: Int, items: [Int], @ViewBuilder content: @escaping (Int) -> Content) {
        self.columns = columns
        self.items = items
        self.content = content
    }
    var rowCount: Int {
        let (q, r) = items.count.quotientAndRemainder(dividingBy: columns)
        return q + (r == 0 ? 0 : 1)
    }
    func elementFor(_ r: Int, _ c: Int) -> Int? {
        let i = r * columns + c
        if i >= items.count { return nil }
        return items[i]
    }
    var body: some View {
        GeometryReader { geo in
            VStack {
                ForEach(0..<self.rowCount) { ri in
                   HStack {
                        ForEach(0..<self.columns) { ci in
                            Group {  
                                if self.elementFor(ri, ci) != nil {
                                    self.content(self.elementFor(ri, ci)!)
                                        .frame(width: geo.size.width / CGFloat(self.columns),
                                               height: geo.size.width / CGFloat(self.columns))
                                } else {
                                    Text("")
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

1 Ответ

0 голосов
/ 27 февраля 2020

GeometryReader - это контейнерное представление, которое определяет его содержимое в зависимости от его собственного размера и координатного пространства. Возвращает гибкий предпочтительный размер в родительский макет.

in

struct ContentView: View {
    var body: some View {
        ScrollView {
            VStack {
                Text("Section 1")
                GridView(columns: 2, items: [1, 3, 5, 7, 9, 11, 13, 15]) { num in
                    Text("Item \(num)")
                }
                .background(Color.red.opacity(0.5))
            }.background(Color.green.opacity(0.2))
        }.background(Color.blue.opacity(0.2))
    }
}

Какая высота у GridView? Hm ... количество строк, умноженное на высоту ячейка сетки, которая является высотой GridView, деленной на количество строк ...

Уравнение не имеет решения: -)

Да, я вижу, вы определили высоту ячейки сетки как ширину GridView делится на количество столбцов, но SwiftUI не такой умный.

Вы должны рассчитать высоту GridView. Я установил число строк в 4, чтобы упростить его ...

struct ContentView: View {
    var body: some View {
        GeometryReader { g in
        ScrollView {
            VStack {
                Text("Section 1")
                GridView(columns: 2, items: [1, 3, 5, 7, 9, 11, 13, 15]) { num in
                    Text("Item \(num)")
                }.frame(height: g.size.width / 2 * CGFloat(4))
                .background(Color.red.opacity(0.5))
            }.background(Color.green.opacity(0.2))
        }.background(Color.blue.opacity(0.2))
        }
    }
}

Вы лучше удалите считыватель геометрии и рамку ячейки сетки из GridView.body и установите размер ячейки в вашем представление содержимого.

struct ContentView: View {
    var body: some View {
        GeometryReader { g in
        ScrollView {
            VStack {
                Text("Section 1")
                GridView(columns: 2, items: [1, 3, 5, 7, 9, 11, 13, 15]) { num in
                    Text("Item \(num)").frame(width: g.size.width / 2, height: g.size .width / 2)
                }
                .background(Color.red.opacity(0.5))
            }.background(Color.green.opacity(0.2))
        }.background(Color.blue.opacity(0.2))
        }
    }
}

Как видите, нет необходимости определять высоту GridView, уравнение теперь имеет решение.

...