HowTO —— SwiftUI 2.0 LazyGrid

Published on

Get weekly handpicked updates on Swift and SwiftUI!

In the first version of SwiftUI, there was no equivalent functionality for UICollectionView. Developers had to wrap it themselves or rely on many third-party libraries. In SwiftUI 2.0, Apple provided Grid widgets through LazyVGrid and LazyHGrid. The implementation of this widget is very much in the style of SwiftUI, and it is significantly different from many third-party libraries.

Basic Usage

Swift
struct GridTest1: View {
    
    let columns = [
        GridItem(.adaptive(minimum: 50))
        //adaptive adjusts itself to fit as many items as possible in a row or column
        //fixed has a fixed size e.g., GridItem(.fixed(50)), and the number of items in each row or column must be explicitly set
        //flexible is similar to fixed, but the size of each item can be adjusted flexibly; the number of items also needs to be set explicitly
        //These types can be mixed
    ]
    
    var body: some View {
        ScrollView{
            LazyVGrid(columns: columns, //row and column settings
                      alignment: .center,
                      spacing: 20,  //spacing between items in a row or column
                      pinnedViews: [.sectionHeaders] 
                      //if there are sections, pin the header or footer during scrolling
            ){
                Section(header:Text("Header")){
                    ForEach(0...1000,id:\.self){ id in
                        Text(String(id))
                            .foregroundColor(.white)
                            .padding(.all, 10)
                            .background(Rectangle().fill(Color.orange))
                    }
                }
            }
        }
    }
}

image-20200709202554829

Mixing LazyVGrid and LazyHGrid

Swift
struct CombineGrid: View {
    
    var body: some View {
        ScrollView{
            LazyVGrid(columns: [GridItem(.adaptive(minimum:40))], alignment: .center, spacing: 10){
                ForEach(0...40,id:\.self){ id in
                    cell(id:id,color:.red)
                }
            }
            //Horizontal scrolling
            ScrollView(.horizontal) {
                LazyHGrid(rows: [GridItem(.fixed(50)),GridItem(.fixed(50))]){
                    ForEach(0...100,id:\.self){id in
                        cell(id:id,color:.green)
                    }
                }
            }
            .frame(height: 240, alignment: .center)
            LazyVGrid(columns: [GridItem(.adaptive(minimum:40))], alignment: .center, spacing: 10){
                ForEach(0...100,id:\.self){ id in
                    cell(id:id,color:.blue)
                }
            }
        }
    }
    
    func cell(id:Int,color:Color) -> some View{
        RoundedRectangle(cornerRadius: 10)
            .fill(color)
            .frame(width: 50, height: 50)
            .overlay(Text("\(id)").foregroundColor(.white))
    }
}

image-20200709205047655

This code displays normally when scrolled up quickly, but the middle LazyHGrid shows anomalies when scrolled up slowly. This is likely a bug. Current environment: Xcode Version 12.0 beta 2 (12 A 6163 b)

Example with Various Parameters Mixed

Swift
import SwiftUI

struct GridTest: View {
    @State var data = (1...1000).map{i in CellView(item:i, width: CGFloat(Int.random(in: 30...100)), height: CGFloat(Int.random(in: 40...80)))}
    
    let column1 = [
        GridItem(.adaptive(minimum: 40, maximum: 80))
    ]
    let column2 = [
        GridItem(.flexible()),
    ]
    let column3 = [
        GridItem(.fixed(100)),
    ]
    
    @State var selection = 1
    @State var alignment:HorizontalAlignment = .leading
    @State var alignmentSelection = 0
    @State var spacing:CGFloat = 10
    var body: some View {
        VStack{
            Picker("", selection: $selection){
                Text("adaptive").tag(0)
                Text("flexible").tag(1)
                Text("fixed").tag(2)
            }
            .pickerStyle(SegmentedPickerStyle())
            .labelsHidden()
            Picker("",selection:$alignmentSelection){
                Text("leading").tag(0)
                Text("center").tag(1)
                Text("trailing").tag(2)
            }
            .pickerStyle(SegmentedPickerStyle())
            .labelsHidden()
            Slider(value: $spacing, in: -100...100

){Text("spacing")}
            Text("\(spacing)")
                .onChange(of: alignmentSelection) { value in
                    switch value{
                    case 0:
                        alignment = .leading
                    case 1:
                        alignment = .center
                    case 2:
                        alignment = .trailing
                    default:
                        break
                    }
                }
            Button("shuffle"){
                withAnimation(Animation.easeInOut){
                    data.shuffle()
                }
            }
            ScrollView{
                let colums = [column1,column2,column3]
                LazyVGrid(columns: colums[selection], alignment: alignment, spacing: spacing, pinnedViews: [.sectionHeaders]){
                    Section(header: Text("header")){
                        ForEach(data,id:\.id){ view in
                            view
                        }
                    }
                }
            }
        }
    }
}

struct CellView:View,Identifiable{
    let id = UUID()
    let item:Int
    let width:CGFloat
    let height:CGFloat
    let colors:[Color] = [.red,.blue,.yellow,.purple,.pink,.green]
    var body: some View{
        Rectangle()
            .fill(colors.randomElement() ?? Color.gray)
            .frame(width: width, height: height, alignment: .center)
            .overlay(Text("\(item)").font(.caption2))
    }
}

**Since it’s a lazy display, if you shuffle before all cells are scrolled into view, the cells that haven’t been created won’t move in an animated way.

Currently, LazyGrid lacks the ability to automatically avoid collisions and cannot implement the Waterfall Grid effect.

Weekly Swift & SwiftUI insights, delivered every Monday night. Join developers worldwide.
Easy unsubscribe, zero spam guaranteed