Implement a collection view SwiftUI

Viewed 6455

How do I implement a collection view (that is, rectangles arranged in a grid) in SwiftUI?

I've tried ForEach-ing over a range of values, then using the counter as an index for an Array but have had numerous problems so are looking for a different way to achieve a collection view.

The cells to wrap over multiple rows. I have a SegmentedControl which is used to set how many cells to squeeze on one row.

Here is what I have:

VStack { // Multiple rows
    ForEach((0..<requiredRows).identified(by: \.self)) { row in // Iterate over (previously calculated) required row count
        HStack(spacing: 50)) { // Row
            ForEach((0..<self.maximumOnXAxis).identified(by: \.self)) { cell in // Iterate over available cells
                MyView(
                    width: calculatedSize,
                    height: calculatedSize / 2.0,
                    colour: Color(
                        red: lst[row * self.maximumOnXAxis][0],
                        green: lst[row * self.maximumOnXAxis][1],
                        blue: lst[row * self.maximumOnXAxis][2]
                    )
                )
            }
        }
    }

And all of the above is inside a ScrollView.

The current problem is a 'Could not type check in reasonable time ...', which happened after I add the colour: Colour(...) parameter to MyView.

Example of desired effect: Collection View Example

2 Answers

SwiftUI does support a ScrollView which you can use to present collections, although you would have to manage your own grids. The GeometryReader method might be helpful, it allows you to easily scale the "cells" to the width and length of the parent view.

For reference, I've created a simple horizontal scroll view. You can use this same logic to create a grid view:

import SwiftUI
import PlaygroundSupport

struct MyView : View {

    let items: [String] = ["1", "2", "3", "4", "5"]
    var body : some View {
        GeometryReader { geometry in
            ScrollView {
                HStack {
                  ForEach(self.items.identified(by: \.self)) { row in
                    Text(row)
                        .padding(geometry.size.width / 5)
                        .background(Color.green)
                  }
                }
            }.background(Color.white)

        }
        .padding()
        .background(Color.red)
    }

}

let vc = UIHostingController(rootView: MyView())
PlaygroundPage.current.liveView = vc

Related