You might be aware of the fact that SwiftUI only supports a List, but nothing
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.
Shoutout to NOAA, National Oceanic and Atmospheric Administration, for such beautiful pictures.
ContentView.swift. You will see an amazing image of aurora
Let’s start our journey of designing a grid layout using Combine!
First, we will create another
View for the grid. Create a new file with the
ImageRow of type SwiftUI View.
Time to understand the logic behind the grid.
- We will have one
ForEachloop for the columns and, inside that, we will have another loop for the rows. Think of it as a 2D array.
- For example, we have a sequence of numbers, 1,2,3,4,5,6. We want to divide the numbers into two columns, so that 1 and 2 are together in one row, 3 and 4 in another, and 5 and 6 in the last row.
- So, we will divide the sequence of numbers in a way that we get chunks** **of them.
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.
Then, create a publisher of integers. In this case, we have 18 images, so:
The magic for the logic arrives. We will use the
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.
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() uses an unbounded amount of memory to store the received
sink(receiveValue:) subscriber to get the whole 2D array from the
publisher. Store the subscription in the images variable.
The whole code should look like this:
Time to write the nested
ForLoop implementation. It is pretty straightforward
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.
Then, inside the loop, add an
HStack for the row elements. Iterating through
each sub-array gives us the number for the image. Modify the image as per your liking.
In the end, your code should look like this:
ContentView.swift,** **remove the image with the modifiers and add the
ImageRow inside a
List with a
navigationView for a nice title bar.
Running the project gives a beautiful grid of landscapes.