본문 바로가기

SwiftUI

SwiftUI) LazyH(V)Grid 알아보기

반응형

저번 시간에 Grid와 GridRow에 대해서 공부하면서 막연했던 Grid에 대해서 감을 잡을 수 있었다. 

오늘은 이어서 LazyH(V)Grid에 대해서 공부해보도록 하겠다. 

 

 

지난번에 LazyStack에 대해서도 공부했는데 "Lazy"가 붙었을 때는 공통점이 있었다.

1. 한번에 모든 하위 뷰를 그리지 않는다.(렌더링을 빠르게 하기 위해)

2. 일반 Stack이나 Grid로 그릴 때, 너무 많은 뷰를 그려 성능이 떨어지는 경우에만 사용해라(Lazy를 먼저 고려하지 말아라)

 

이번 LazyGrid 역시도 마찬가지였다. 우선 공식문서의 내용을 더 살펴보자 

 

LazyHGrid

수평으로 증가하는 그리드에서 하위 뷰를 정렬하고 필요한 경우에만 항목을 생성하는 컨테이너 뷰

  • 2차원 레이아웃으로 배열된, 크고 수평으로 스크롤 할 수 있는 뷰들을 표시하려면 LazyHGrid를 사용해라 
  • 그리드를 그리는 순서는 첫 번째 열에서 위에서부터 밑으로 그리고 다음 열로 넘어가 그리는 방식이다. 
  • 수평으로 열 수는 무제한으로 증가할 수 있지만 행 수는 GridItem 인스턴스를 통해 결정된다. 

Grid만을 이용해서도 구현이 가능하다. 또한 Grid는 모든 하위 뷰를 즉시 생성하는 장점이 있기 때문에 성능이 떨어지는 경우에만 LazyHGrid를 사용해라.

 

LazyHGrid의 선언부는 다음과 같이 구성되어 있다. 

  • rows: 그리드의 각 열의 크기와 위치를 지정하는 GridItem의 배열이다. (GridItem은 다음 글에 다루도록 하겠다.)
  • alignment: 상위 뷰 내애서 그리드 정렬을 할 수 있는 파라마터이다.(HGrid이기 때문에 상하로의 정렬일 것이다.)
  • spaing: 항목 간의 간격을 설정한다.
  • pinnedViews: uikit의 Sticky Header와 같이 스크롤할 때 하위 뷰를 고정시키는 역할을 한다. 

 

 

이제 Grid와 LazyHGrid를 이용해서 비슷하게 한번 만들어보고 코드를 비교해 보겠다. 

struct GridPractice6View: View {
    let rows = [GridItem(.fixed(30)), GridItem(.fixed(30))]
    var body: some View {
        VStack {
            // MARK: - Gridd와 GridRow 사용 
            ScrollView(.horizontal) {
                Grid(horizontalSpacing: 40) {
                    GridRow() {
                        ForEach(0x1f600...0x1f679, id: \.self) { value in
                            Text(String(format: "%x", value))
                        }
                    }
                    GridRow() {
                        ForEach(0x1f600...0x1f679, id: \.self) { value in
                            Text(emoji(value))
                                .font(.largeTitle)
                        }
                    }
                }
            }
            // MARK: - LazyHGrid 사용
            ScrollView(.horizontal) {
                LazyHGrid(rows: rows, spacing: 40, pinnedViews: .sectionHeaders) {
                    ForEach(0x1f600...0x1f679, id: \.self) { value in
                        Text(String(format: "%x", value))
                        Text(emoji(value))
                            .font(.largeTitle)


                    }
                }
            }
        }
    }
    private func emoji(_ value: Int) -> String {
        guard let scalar = UnicodeScalar(value) else { return "?" }
        return String(Character(scalar))
    }
}

(완벽하게 같지는 않지만) Grid와 GridRow를 사용하는 것과 LazyGrid를 사용하는 것에서 코드의 난이도는 사실 비슷해 보인다. 

또한 어떤 것을 사용하든 원하는 결과물을 만들 수 있다. 하지만 여기서 어떤 것을 사용해야 할 것인지의 고민은 

우선은 Grid와 GridRow를 사용하고, 렌더링의 성능 저하가 보인다면 LazyHGrid를 사용하는 것이 좋다고 한다. 

 

반응형

다음으로는 LazyVGrid이다. 사실 LazyVGrid 역시 별로 차이가 없다. 

LazyVGrid

수직으로 증가하는 그리드에서 하위 뷰를 정렬하고 필요한 경우에만 항목을 생성하는 컨테이너 뷰

  • 2차원 레이아웃으로 배열된, 크고 수직으로 스크롤 할 수 있는 뷰들을 표시하려면 LazyVGrid를 사용해라 
  • 그리드를 그리는 순서는 첫 번째 행에서 좌에서부터 우로 그리고 다음 행로 넘어가 그리는 방식이다. 
  • 수직으로 열 수는 무제한으로 증가할 수 있지만 열 수는 GridItem 인스턴스를 통해 결정된다. 

마찬가지로 Grid를 사용해서 원하는 결과물을 만들 수 있기 떄문에 우선 Grid를 고려하고 성능 저하를 발생시키는 경우에 LazyVGrid를 사용하는 것이 좋다. 

 

LazyVGrid의 선언부도 거의 유사하게 구성되어 있다.

이번에는 예시 코드로 pinnedView를 구현해보겠다. 

struct GridPractice4_View: View {
    private var data : [Int] = Array(1...50)
    private var colors: [Color] = [.red, .green, .blue, .yellow]
    private let adaptiveColums = [
        GridItem(.adaptive(minimum: 100))
    ]
    
    var body: some View {
            ScrollView {
                LazyVGrid(columns: adaptiveColums, spacing: 20, pinnedViews: [.sectionHeaders]) { //pinnedView를 사용
                    Section {
                        ForEach(data, id: \.self) { number in
                            ZStack {
                                Rectangle()
                                    .frame(width: 100, height: 100)
                                    .foregroundColor(colors[number % 4])
                                    .cornerRadius(30)
                                Text("\(number)")
                                    .foregroundColor(.white)
                                    .font(.system(size: 80, weight: .medium, design: .rounded))
                            }
                        }
                    } header: {
                        //HeaderView
                        SectionView()
                    }

                }
            }
            .padding()
        }
}

Section을 나타내기 위한 View

struct SectionView: View {
    var body: some View {
        HStack {
            Text("Section")
                .font(.headline)
                .foregroundColor(.black)
            Spacer()
        }
        .padding()
        .background(Color.primary
                        .colorInvert()
                        .opacity(0.75))
    }
}

스크롤을 내려도 pinnedView가 상단에 고정되어 있는 것을 알 수 있다. 

 

PinnedScrollableViews의 선언을 보면 다음과 같다. 

섹션의 헤더뷰 or 푸터뷰가 고정되어 나타난다고 한다. 

 

 

 

(사실 GridItem을 더 집중적으로 공부해보려고 했다...근데 생각보다 글이 길어져 다른 글로 빼고)

LazyGrid를 사용하는 것을 고민한다면 과연 성능이 어떤지 먼저 파악할 필요가 있을 것 같다. 

 

 

 

Apple Developer Documentation

 

developer.apple.com

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

 

Apple Developer Documentation

 

developer.apple.com

 

반응형