SwiftUI Layout - LazyVGrid & LazyHGrid

LazyVGrid and LazyHGrid, one is a vertical grid layout and the other is a horizontal grid layout. The new layout manager of iOS14, like LazyVGrid and LazyHStack with Lazy, indicating that Layout managers will only drawing the child elements when need to be displayed.

LazyVGrid

LazyVGrid fills the layout by specifying the number of columns.

01 02
img img

The filling order of LazyVGrid is from the top row by row, and when one row is filled, will fill the next row. It’s easy to understand from the alphabetical displayed in the above figure.

The Code of Left Picture

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
var columns: [GridItem] = [
    GridItem(.fixed(100)),
    GridItem(.fixed(100)),
    GridItem(.fixed(100))
]
    
var body: some View {
	// you can use colums array or directory write code in the lines.
	// LazyVGrid(columns: columns) {
    LazyVGrid(columns: [GridItem(.fixed(100)), GridItem(.fixed(100)), GridItem(.fixed(100))]){
         ForEach(0 ..< 30){ index in
             RoundedRectangle(cornerRadius: 5)
                 .foregroundColor(Color(hue: 0.03 * Double(index) , saturation: 1, brightness: 1))
                 .frame(height: 50)
                 .overlay(Text("\(String(Unicode.Scalar(65 + index)!))"))
         }
     }
     .padding()
}

The Code of Left Picture

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// The row spacing can be adjusted by setting the spacing of LazyVGrid

var body: some View {
    LazyVGrid(columns: [GridItem(.fixed(100)), GridItem(.fixed(100)), GridItem(.fixed(100))], spacing: 30){
      ForEach(0 ..< 30){ index in
          RoundedRectangle(cornerRadius: 5)
              .foregroundColor(Color(hue: 0.03 * Double(index) , saturation: 1, brightness: 1))
              .frame(height: 50)
              .overlay(Text("\(String(Unicode.Scalar(127881 + index)!))"))
      }
    }
    .padding()
}

LazyVGrid can make the Header and Footer hover at the top and bottom when scrolling by setting the PinnedViews type. Set the header and footer of Section through Section.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
VStack {
  ScrollView {
      LazyVGrid(columns: [GridItem(.fixed(100)), GridItem(.fixed(100)), GridItem(.fixed(100))], pinnedViews: [.sectionHeaders, .sectionFooters]){
          ForEach(0 ..< 5){ index in
              Section(header: Text("Header \(index)")
                          .bold()
                          .font(.title)
                          .frame(maxWidth: .infinity, alignment: .leading)
                          .background(Color.white),
                      footer: Text("Footer \(index)")
                              .bold()
                              .font(.title)
                              .frame(maxWidth: .infinity, alignment: .leading)
                              .background(Color.white)
              ) {
                  ForEach(0 ..< 10){ idx in
                      RoundedRectangle(cornerRadius: 5)
                          .foregroundColor(Color(hue: 0.03 * Double(index * 10 + idx) , saturation: 1, brightness: 1))
                          .frame(height: 50)
                          .overlay(Text("\(index * 10 + idx)"))
                  }
              }
          }
      }
      .padding()
  }
}
.clipped()
01 02
img img

GridItem

GridItem controls the width by the GridSize

Enumeration Cases

GeometryReader is a special View that can get coordinate size information.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
var body: some View {
    LazyVGrid(columns: [GridItem(.flexible()), GridItem(.adaptive(minimum: 50), spacing: 0)]){
        ForEach(0 ..< 30){ index in
            GeometryReader{ proxy in
                RoundedRectangle(cornerRadius: 5)
                    .foregroundColor(Color(hue: 0.1 * Double(index) , saturation: 1, brightness: 1))
                    .overlay(Text("\(proxy.size.width)"))
            }
            .frame(height: 50)

        }
    }
    .padding()
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
var body: some View {
    LazyVGrid(columns: [GridItem(.flexible(), spacing: 10), GridItem(.flexible(), spacing: 100), GridItem(.flexible(), spacing: 50)]){
        ForEach(0 ..< 30){ idx in
            RoundedRectangle(cornerRadius: 5)
                .foregroundColor(Color(hue: 0.0333 * Double(idx) , saturation: 1, brightness: 1))
            .frame(height: 50)
        }
    }
    .padding()
}

We can see from the running effect that only the set space will be added to the right side of the grid.

Effect
img

Alignment

Alignment: use this property to anchor the view’s relative position to the same relative position in the view’s assigned grid space. Similar to the usage of frame's Alignment.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
var body: some View {
    LazyVGrid(columns: [GridItem(.flexible(), alignment: .topLeading), GridItem(.flexible(), alignment: .bottomLeading), GridItem(.flexible(),  alignment: .trailing)]) {
        ForEach(0 ..< 30, id: \.self){ index in
            RoundedRectangle(cornerRadius: 5)
                .foregroundColor(Color(hue: 0.01 * Double(index * 10), saturation: 1, brightness: 1))
                .frame(width: CGFloat((index % 3) + 1) * 20, height: CGFloat((index % 3) + 1) * 20)
        }
    }
    .padding()
}
Effect
img

LazyHGrid

LazyHGrid and LazyVGrid are simliar in uasge. LazyHGrid specified the number of rows, fill the specified number of rows from top to bottom, and then fills the second column.

How to achieve an irregular

Effect
img
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
var body: some View {
    LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible())]){
          ForEach(0 ..< 11){ idx in
              GeometryReader { r in
                  RoundedRectangle(cornerRadius: 5)
                      .foregroundColor(Color(hue: 0.1 * Double(idx) , saturation: 1, brightness: 1))
                      .frame(width: idx == 6 ? 2 * r.size.width  + 10 : r.size.width )
              }
              .frame(height: 100)
              if idx == 6 {
                  Color.clear
              }
          }
      }
      .padding()
}
Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy