Playing With Combine: Grid Layout in SwiftUI

A basic implementation of grid in SwiftUI using Combine

Rudrank Riyam
Sep 9, 2019 · 4 min read
Image for post
Image for post
Mockup generated by https://mockuper.net

You might be aware of the fact that SwiftUI only supports a List, but nothing like a collectionView natively. I have been learning about Combine and thought of trying to implement something similar.

Here’s the demo project to start with.

When you open the project, go and explore the 18 pleasing landscapes, named as noaa[number] for the sake of simplicity.

Image for post
Image for post

Shoutout to NOAA, National Oceanic and Atmospheric Administration, for such beautiful pictures.

Open ContentView.swift. You will see an amazing image of aurora australis.

Let’s start our journey of designing a grid layout using Combine!

Image for post
Image for post

First, we will create another View for the grid. Create a new file with the name ImageRow of type SwiftUI View.

Image for post
Image for post

Time to understand the logic behind the grid.

  • We will have one ForEach loop for the columns and, inside that, we will have another loop for the rows. Think of it as a 2D array.

Coding

Time to code it out.

The sequence can be anything, from the name of the images to normal text. Here, we are considering the integer numbers for the NOAA images.

We will create an empty 2D array to store the chunked values.

var images: [[Int]] = []

Then, create a publisher of integers. In this case, we have 18 images, so:

_ = (1...18).publisher

The magic for the logic arrives. We will use the collect() instance method. Quoting from Apple’s documentation:

“Collects all received elements, and emits a single array of the collection when the upstream publisher finishes.”

You can specify the number of values you want the collection to limit to, and we will use it for specifying the number of columns. This gives us a stream of array chunks.

.collect(2) // Creating two columns
Image for post
Image for post
The two columns

Now, we need to have these arrays of rows in a single 2D array. We will have another collect() method to get all the values in a single stream.

.collect()
Image for post
Image for post
The stream in one single 2D array

Note: collect() uses an unbounded amount of memory to store the received values.

Use the sink(receiveValue:) subscriber to get the whole 2D array from the publisher. Store the subscription in the images variable.

.sink(receiveValue: { images = $0 })

The whole code should look like this:

Image for post
Image for post

Time to write the nested ForLoop implementation. It is pretty straightforward from now.

First, we will have a ForEach loop for the number of rows. Iterate from zero to the number of sub-arrays in images, i.e. the number of rows.

return ForEach(0..<images.count, id: \.self) { array in

Then, inside the loop, add an HStack for the row elements. Iterating through each sub-array gives us the number for the image

HStack {    ForEach(images[array], id: \.self) { number in

Modify the image as per your liking.

Image(“noaa\(number)”)    .resizable()    .scaledToFit()    .cornerRadius(10)

In the end, your code should look like this:

Image for post
Image for post

In ContentView.swift, remove the image with the modifiers and add the ImageRow inside a List with a navigationView for a nice title bar.

NavigationView {    List {        ImageRow()    }.navigationBarTitle(Text("Landscapes"))}

Running the project gives a beautiful grid of landscapes.

Image for post
Image for post

Better Programming

Advice for programmers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store