[SwiftUI] - LazyVGrid & LazyHGrid

오늘은 새로운 인프런 강의를 뚫었다.. 배우는데 돈 아까워 하지말자 ㅎㅎ SwiftUI를 배우고 있는데 이전과는 달리 중요한 요소 하나하나 조금 자세하게 파고들어 공부하고 싶어서 요즘 다시 기본기를 채우기 위해 자세하게 알아보고 있다.

 

먼저 SwiftUI에서 Grid는 UIKit에서 많이 사용했던 CollectionView와 거의 흡사하다고 보면 된다. SwiftUI의 CollectionView인 느낌이랄까!

 

Grid라는 의미 자체는 2차원의 행과 열을 구성하는 레이아웃이라는 뜻을 가지고 있다.

https://developer.apple.com/documentation/swiftui/grid/

 

Grid는 두 가지로 나눠져 있다 바로 VGrid , HGrid이다.

 

LazyVGrid

V라는게 붙은 다른 것과 마찬가지로 세로로 열이 우선되게 뷰가 정렬되고 쌓이는 컨테이너 뷰이다.

 

Lazy가 붙은 이유는 필요할때 해당 SubView를 정렬해 생성하기 때문에 Lazy가 붙었다고 한다.

 

LazyVGrid를 간단하게 그려보자

LazyVGrid(
      columns: columns,
      alignment: .center,
      spacing: 6,
      pinnedViews: [.sectionHeaders]) {
          // Section 1
          Section(header: Text("section1")
              .foregroundStyle(.white)
              .font(.title)
              .frame(maxWidth: .infinity, alignment: .leading)
              .background(Color.blue)
              .padding()
          ) {
              ForEach(0..<20) { index in
                  Rectangle()
                      .fill(Color.gray)
                      .frame(height: 150)
                      .overlay(
                          Text("\\(index) 번")
                      )
              }
          } //: Section 1
          
          // Section 2
          Section(header: Text("Section2")
              .foregroundStyle(.white)
              .font(.title)
              .frame(maxWidth: .infinity, alignment: .leading)
              .background(Color.green)
              .padding()
          ) {
              ForEach(0..<20) { index in
                  Rectangle()
                      .fill(Color.gray)
                      .frame(height: 150)
                      .overlay(
                          Text("\\(index) 번")
                      )
              }
          } //: Seciton 2
          
          // Section 3
          Section(header: Text("Section3")
              .foregroundStyle(.white)
              .font(/*@START_MENU_TOKEN@*/.title/*@END_MENU_TOKEN@*/)
              .frame(maxWidth: .infinity, alignment: .leading)
              .background(Color.yellow)
              .padding()
          ) {
              ForEach(0..<20) { index in
                  Rectangle()
                      .fill(Color.gray)
                      .frame(height: 150)
                      .overlay(
                          Text("\\(index) 번")
                      )
              }
          } //: Section 3
      }
} // : 1번 끝

이렇게 VGrid를 통해서 섹션을 나눠 구현이 가능하고 섹션의 헤더 부분을 pinnedViews 에 넣어주면 영상과 같이 헤더를 고정시키고 다음 헤더가 왔을때 넘기는 내용을 아주 쉽게 구현할 수 있다.

UIKit으로 이 내용을 구현한다고 생각하니 음.. 이렇게 보면 SwiftUI가 생산성이 참 좋은 것 같다.

 

아무튼 위에 코드를 조금 더 자세하게 알아보자

 

GridItem

그리드아이템은 그리드의 행 혹은 열에 대한 디스크립션이라고 보면 된다.

그리드를 처음 만들때 GridItem 타입의 컴포넌트를 먼저 만들어서 넣어줘야 한다.

let columns: [GridItem] = [
    GridItem(.flexible(), spacing: 6, alignment: nil),
    GridItem(.flexible(), spacing: 6, alignment: nil),
    GridItem(.flexible(), spacing: 6, alignment: nil),
    GridItem(.flexible(), spacing: 6, alignment: nil)
]

이 중에서 행 혹은 열의 값을 넣을 수 있고 초기화 하면서 파라미터는 alignment(정렬), spacing(간격), size(크기)가 있다.

 

이중에서 size를 더 자세히 들여다보면 총 3가지의 케이스가 있다.

 

  • adaptive(minumum: CGFloat, maximum: CGFloat)

최소값과 최대값을 정하고 이 사이의 사이즈로 가장 많이 배치 가능하게 할 때 사용

 

  • flexible(minumum: CGFloat, maximum: CGFloat)

최소, 최대값을 정해두고 뷰 크기에 따라 사이즈를 조절할 수 있다.

인자에 아무것도 주어지지 않으면 해당 뷰의 크기를 아이템의 수로 나눠 계산한다

 

  • fixed(CGFloat)

고정된 사이즈로 아이템을 지정해 준다.

 

이것을 토대로 위에 코드로 LazyVGrid를 만들게 되면 사진과 같이 4개의 행을 가지고 뷰 크기를 아이템 수에 맞춘 뒤, 간격을 6을 준 VGrid가 만들어 지게 된다.

 

여기서 행을 줄이거나 더 늘리고 싶다면 GridItem을 줄이거나 늘리면 된다!

 

아주 쉽게 UIKit의 CollectionView를 구현할 수 있다는 점이 아주 흥미롭다.

 

LazyHGrid

다음은 HGrid에 대해서 알아보자.

 

H는 수평으로 늘어다는 그리드에 SubView를 정렬해 나타낼 수 있는 컨테이너 뷰이다.

 

LazyVGrid와 차이는 열은 무수히 증가해서 구성할 수 있고, 행은 최대 얼마나 할지 지정할 수 있다는 점이다.

 

VGrid와 반대로 인식하면 편하다.

let title: [String] = Array(1...1000).map {"목록 \\($0)"}
let layout: [GridItem] = [
    GridItem(.flexible(maximum: 80)),
    GridItem(.flexible(maximum: 80))
]

먼저 그리드에 들어갈 데이터를 만들어 보자 가로로 무수히 만들 수 있다는 점을 보여주기 위해 1000개로 설정해보았다.

 

그 다음 VGrid와 반대로 열의 갯수를 지정하기 위해 GridItem의 컴포넌트를 만들어 넣어주었다.

ScrollView(.horizontal) {
    LazyHGrid(rows: layout, spacing: 20) {
        ForEach(title, id: \\.self) { item in
            VStack {
                Capsule()
                    .fill(Color.yellow)
                    .frame(height: 30)
                
                Text(item)
                    .foregroundColor(/*@START_MENU_TOKEN@*/.blue/*@END_MENU_TOKEN@*/)
            }
        }
    }
}

그 다음 스크롤 뷰 안에 LazyHgrid를 넣은 뒤 이미 만들어놨던 요소를 넣어주게 되면 가로로 구성되어있는 grid를 쉽게 구현할 수 있게 된다.

 

이렇게 UIkit의 CollectionView로 구현하려면 굉장히 복잡하고 번거로운 과정을 SwiftUI에서는 간편하게 구현할 수 있다는 점을 배웠다.

 

같은 방법을 쉽게 할 수 있다는 점이 처음 공부할때보다 더더더더 재밌게 접근할 수 있는 것 같다 ㅎㅎ

그럼 오늘은 여기까지!