SwiftUI - How to create a Grid View in iOS 13?

Grid iOS 13 Compressed.png

After SwiftUI was introduced, All the people who adopted it fell in love with it. Even the people not using it were excited to try it on full fledge.

SwiftUI this year has improved beyond imagination, and we all know there’s going to be so much more. But with that in mind, we know we can’t lose iOS 13 from our Deployment Target. Grid View is finally available in iOS 14, but it doesn’t support iOS 13.

So let’s look at a way of creating a Grid View in iOS 13.

Please click here to download the starter project.

Let’s Start

Open the starter project you have downloaded, and Run the application.

You must already be having a Grid View up and running in your Canvas or Simulator or iPhone.

Let me explain to you how I made this Custom Grid View.

Open your ContentView.swift file, the ContentView struct should look something like this.

struct ContentView: View {
    
    // Fetching List of Emojis from Emoji.swift file.
    let emojiList: [String] = Emoji.emoji
    
    var body: some View {
        
        NavigationView {
        
            // Vertical ScrollView for the Grid layout
            ScrollView() {
                
                // Custom Grid View
                UIGrid(columns: 4, list: emojiList) { emoji in
                    
                    Text(emoji)
                        .font(.largeTitle)
                        
                }
            }
            
            .navigationBarTitle("Smileys")
        }
    }
}

The important code goes in the body where we have declared the Custom Grid View.

UIGrid takes two arguments, the number of columns you need to represent, and a list you want to populate. UIGrid block will iterate all the objects for you to render it the way you want to.

This code here is simple, but let’s look into the UIGrid.swift file.

Let’s do it bit by bit so that it’s easy for you to understand.

// 'Content' here is used to store the View you defined in the Block
// 'T' is the object type of your list.
struct UIGrid<Content: View, T: Hashable>: View {
    
    private let columns: Int
    
    // Multi-dimensional array of your list. Modified as per rendering needs.
    private var list: [[T]] = []
    
    // This block you specify in 'UIGrid' is stored here
    private let content: (T) -> Content
    
    init(columns: Int, list: [T], @ViewBuilder content:@escaping (T) -> Content) {
        self.columns = columns
        self.content = content
        self.setupList(list)
    }

If you look at the declaration of the struct UIGrid<Content: View, T: Hashable>: View, the Content is used to store the View you defined previously in the UIGrid block which is Text(emoji).font(.largeTitle). Next, the generic T defines the type of list you are going to pass.

We have declared thee essential properties columns, list, and content, which is initialized from the Content.swift file with the help of the initializer defined here.

If you look into the code of initializer, the list is being asked to set up from a different function, as we have columns to represent, which here means different Stacks, we need to modify the list received in the initializer as per our needs of the layout.

Let’s look into the setupList(_:) function.

// setupList(_:) Converts your array into multi-dimensional array.
private mutating func setupList(_ list: [T]) {
    var column = 0
    var columnIndex = 0
    
    for object in list {
        if columnIndex < self.columns {
            if columnIndex == 0 {
                self.list.insert([object], at: column)
                columnIndex += 1
            }else {
                self.list[column].append(object)
                columnIndex += 1
            }
        }else {
            column += 1
            self.list.insert([object], at: column)
            columnIndex = 1
        }
    }
}

This function helps us create a multi-dimensional array, based on columns we define.

E.g., As we have defined 4 columns in the initializer, our list object will contain no more than 4 elements at all indexes of the array.

It’s time for the body.

// The Magic goes here
var body: some View {
    VStack {
        ForEach(0 ..< self.list.count, id: \.self) { i  in
            HStack {
                ForEach(self.list[i], id: \.self) { object in
                    
                    // Your UI defined in the block is called from here.
                    self.content(object)
                        .frame(width: UIScreen.main.bounds.size.width/CGFloat(self.columns))
                }
            }
        }
        
    }
}

The body iterates our multi-dimensional array representing a list of HStack (Horizontal Stacks), holding four elements in each stack. Each stack calls the Content we specified in the UIGrid block, and our data is represented.

This is how we make our Grid View.

Grid iOS 13 Demo Gif.gif

You can modify this file as per your needs. Create a Horizontal Grid View, customize UI, or just play with it. And don’t forget to tell me about your experience on Twitter.

I hope you guys enjoyed reading my article. There’s a lot more to learn together, so subscribe to stay updated about my upcoming articles.

If you have any suggestions or questions, Feel free to connect me on Twitter or Reddit. 😉

To Read, SwiftUI - How to use LazyVGrid and LazyHGrid in iOS 14?, Click Here.

How to create Custom Animations in SwiftUI?

SwiftUI - How to use LazyVGrid and LazyHGrid in iOS 14?