THIS IS ELLIE

SwiftUI - VStack, LazyVStack 비교하기 본문

개발/SwiftUI

SwiftUI - VStack, LazyVStack 비교하기

Ellie Kim 2022. 7. 31. 02:31

안녕하세요.
오랜갑만입ㄴ다.

오늘은 VStack과 LazyVStack에 대해서 포스팅하려 합니다.


먼저 VStack은 익숙하시죠?
(모르겠으면 여기)
2020.05.28 - [개발/SwiftUI] - SwiftUI 유형별 스택 VStack, HStack, ZStack

 

ㅇㅇ VStack을 사용하면 수직으로 뷰를 쌓을 수 있습니다.
간단한 예로 텍스트 두 개 생성해서 VStack에 넣어봅시다.

struct ContentView: View {
    var body: some View {
        VStack {
            Text("안녕")
            Text("하세요")
        }
    }
}

안녕, 하세요를 VStack안에 넣어주면?
아래와 같이 세로로 텍스트가 노출되게 됩니다.

여기까지 오케이!


그럼 LazyVStack은 언제 쓰는데?
애플 공식 문서에 따르면!
스택은 lazy 하기 때문에 아이템들이 화면에 렌더링 될 때 생성됩니다.
뭐 그렇다고 합니다.
(이게 머선 말이고?)

애플은 아래와 같은 예시를 보여줍니다.
예시는 ScrollView안에 LazyVStack이 있고 ForEach를 통해 1에서 100까지 글자가 쓰인 뷰를 만들어냅니다.

ScrollView {
    LazyVStack(alignment: .leading) {
        ForEach(1...100, id: \.self) {
            Text("Row \($0)")
        }
    }
}

실행하면 아래와 같이 나옵니다. 

여기까지만 봐서 위에서 말한
스택은 lazy 하기 때문에 아이템들이 화면에 렌더링 될 때 생성됩니다?
라는 말이 이해가 잘 안되지 않나요?

아이템들이 화면에 렌더링 될 때 생성된다?
위 예제에서 100개 중 화면에 Row 39까지 렌더링 되니까 
생성되는 시점에 프린트를 찍어도 39까지만 생성되겠지?
그래서 생성되는 시점에 프린트를 찍어보려 합니다.



테스트용으로 간단한 Row뷰를 생성해줍니다.

struct Row: View {
    let id: Int

    var body: some View {
        Text("Row \(id)")
    }

    init(id: Int) {
        print("Row \(id)")
        self.id = id
    }
}

생성될 때 즉 init 시점에 Row와 id를 출력하도록 했으니
어디까지 생성되었는지 파악할 수 있게 됩니다.

애플 예제에서 코드 조금만 더 추가해봅시다.

struct Row: View {
    let id: Int

    var body: some View {
        Text("Row \(id)")
    }

    init(id: Int) {
        print("Row \(id)")
        self.id = id
    }
}

struct ContentView: View {
    var body: some View {
        ScrollView {
            LazyVStack(alignment: .leading) {
                ForEach(1...100, id: \.self, content: Row.init)
            }
        }
    }
}

그러면 우리가 테스트하고 싶은 부분인 생성 시점을 파악할 수 있겠죠?

39까지 노출되고

39개만 생성되는 줄 알았는데 40까지 생성되었네요?
오 여유 있게 만들어주나 봄.

그럼 스크롤하면 아래 40부터 쭉쭉 보이겠죠?
맨 끝까지 ㄱㄱ

스크롤을 끝까지 하니 Row 100이 찍히게 됩니다.

그럼 다시 위 말을 생각해봅시다.
스택은 lazy 하기 때문에 아이템들이 화면에 렌더링 될 때 생성됩니다.
ㅇㅎ 보이는 것만 생성된다!
뭔가 감이 조금씩 잡히는 것 같기도 하고?


그럼 여기서 드는 궁금증!
아래 코드와 같이 ScrollView안에 그냥 VStack이 있으면요?

struct ContentView: View {
    var body: some View {
        ScrollView {
            VStack(alignment: .leading) {
                ForEach(1...100, id: \.self, content: Row.init)
            }
        }
    }
}

VStack은 LazyVStack과 비교해서 어떤 차이점이 있을까요?

차이점 1 정렬이. leading임에도 불구하고 중간에 텍스트가 있다?

 

그리고 39까지 밖에 안 보이는데 Row 100 출력되어 있음.

차이점 2 아이템들이 렌더링 되지 않았음에도 불구하고 한 번에 다 생성된다?  

 

하나씩 알아봅시다.
먼저 차이점 1 정렬이. leading임에도 불구하고 중간에 텍스트가 있는 느낌.
좀 더 명확히 테스트하기 위해 아래와 같이 VStack과 LazyVStack에 파란 배경색을 추가해줍시다.

struct ContentView: View {
    var body: some View {
        ScrollView {
            VStack(alignment: .leading) {
                ForEach(1...100, id: \.self, content: Row.init)
            }.background(.blue)
        }
    }
}
struct ContentView: View {
    var body: some View {
        ScrollView {
            LazyVStack(alignment: .leading) {
                ForEach(1...100, id: \.self, content: Row.init)
            }.background(.blue)
        }
    }
}

오???? 

왼쪽이 VStack 그리고 오른쪽이 LazyVStack의 결과입니다.
아래와 같이 스크롤 바의 위치도 다르다는 것을 확인할 수 있습니다. (ㅇㅇ착한 사람들한테만 보이는 스크롤 바)

찾아보니 LazyVStack은 자동으로 유연한 선호 너비를 가져서 일반 스택과 달리 여유 공간을 차지한다고 합니다. 
ㅇㅋㅇㅋ

 

그럼 다음으로 차이점 2 아이템들이 렌더링 되지 않았음에도 불구하고 한 번에 다 생성된다?
VStack은 화면에 렌더링과는 상관없이 한 번에 생성됩니다. 
LazyVStack은 아이템들이 화면에 렌더링 될 때 생성됩니다.

한 번에 생성되나? 화면에 렌더링 될 때 생성되나? 그게 중요하려나 생각들 수 있겠지만
Row의 수가 많아지면 어떨까요?
만약에 100개가 아니라 1000개면?
만약에 1000개가 아니라 10000개면?
만약에 10000개가 아니라 100000개면?
만약에 100000개가 아니라 1000000개면?
ㅇㅇ문제가 될 것 같죠!

그리고 심지어 VStack으로 다 만들어놨음.
그리고 사용자가 스크롤하지 않을 수도 있겠죠?
더 최악.
CPU, 메모리가 많이 낭비되겠죠.

그럼 아래 코드로 메모리 사용량이 얼마나 차이가 나는지 확인해봅시다.

struct ContentView: View {
    var body: some View {
        ScrollView {
            VStack(alignment: .leading) {
                ForEach(1...1000, id: \.self, content: Row.init)
            }
        }
    }
}
struct ContentView: View {
    var body: some View {
        ScrollView {
            LazyVStack(alignment: .leading) {
                ForEach(1...1000, id: \.self, content: Row.init)
            }
        }
    }
}

 

아래 왼쪽 이미지가 VStack으로 실행한 결과.
아래 오른쪽 이미지가 LazyVStack으로 실행한 결과.

차이가 꽤 크죠?
그래서 최소한 화면에 노출되는 것만 뷰를 로드하고 스크롤할 때 추가로 뷰를 로드하는 것이 좋습니다.
이것이 LazyVStack의 역할이고 사용하는 이유입니다.


resource: 
https://developer.apple.com/documentation/swiftui/lazyvstack
https://www.hackingwithswift.com/quick-start/swiftui/how-to-lazy-load-views-using-lazyvstack-and-lazyhstack

반응형

'개발 > SwiftUI' 카테고리의 다른 글

SwiftUI - Alert  (0) 2022.08.17
SwiftUI - Rectangle, RoundedRectangle  (0) 2022.08.15
SwiftUI - Text / 마크다운  (2) 2022.07.09
SwiftUI - Image  (0) 2022.07.09
SwiftUI - Spacer  (2) 2022.03.27