klioop for iOS

SwiftUI 레이아웃 시스템 2, Stack 본문

SwiftUI

SwiftUI 레이아웃 시스템 2, Stack

klioop2@gmail.com 2021. 5. 22. 02:02

안녕하세요

 

지난번에 이어서 SwiftUI 레이아웃 시스템 원리에 대해서 공부해 볼게요.

Stack(VStack, HStack) 안에서 뷰들이 어떻게 정렬되는지 알아보겠습니다.

 

struct StackExample: View {
    var body: some View {
        
        
        VStack {
            
            HStack {
                
                Text("Cute")
                Image("cat_100x100")
                Text("baby cat")
                
            }
            .border(Color.blue)
            
        }
        .frame(width: 300, height: 200)
        .border(Color.red)
        
    }
}

 

코드 결과

 

VStack 은 일단 부모 뷰가 크기를 제안하는 용도로 생각하고

HStack 안에서 뷰들이 어떻게 정렬되는지 알아보겠습니다.

 

빨강색 보더라인이 부모 뷰가 제안하는 크기이고 

파랑색 보더라인이 HStack 이 결정한 크기입니다.

 

https://developer.apple.com/videos/play/wwdc2019/237 를 보면 Stack 의 경우

주어진 크기를 가지고 자식 뷰들이 경쟁한다는 측면에서 흥미롭다는 이야기가 나옵니다.

 

두 가지 경우를 살펴보고 다시 돌아와서 생각해볼게요.

그리고 다음의 레이아웃 원칙을 항상 생각해야 합니다.

 

  1. 부모 뷰는 자식 뷰에게 자신의 사이즈를 그대로 제안한다.
  2. 자식 뷰는 부모의 사이즈와 함께 자신의 display contents 를 고려해서 자신의 사이즈를 정한다. 
  3. 부모는 자식이 결정한 사이즈를 가지고 자식을 자신의 좌표공간에 놓는다. 

https://klioop.tistory.com/28 에서 자세히 설명했습니다.

 

먼저 위 코드처럼 부모 뷰가 Stack 에 제안하는 영역이 충분한 경우입니다.

부모 뷰는 HStack 에 300x200 의 크기를 제안하면 

 

  1. HStack 은 자신의 자식 뷰들 끼리 가져야 하는 Spacing 크기만큼을 제안받은 크기에서 뻅니다.
    여기에서는 Text("Cute") 와 이미지 사이에 Spacing, 그리고 이미지와 Text("baby cat") 사이 Spacing 의 크기를 더한 값이
    되겠네요. 예를 들면, 두 Spacing 을 합쳐서 30 이라고 하면 제안받은 너비 300 에서 30 을 뺀 270 이 남습니다.

  2. 그 다음 HStack 은 자식 뷰들의 수에 맞게 똑같이 크기를 나눕니다.
    여기에서는 자식 뷰가 3 개이니까 너비를 90 씩 3 개로 나눕니다. (높이는 공통되니 생략하겠습니다)

  3. HStack 은 이제 한 자식 뷰에게 너비 90을 제안합니다.
    그런데 자식 뷰들 사이에서 우선순위가 같다면, 가장 경직적인 자식 뷰 부터 제안을 받습니다.
    여기에서는 Image 뷰가 가장 경직적입니다.
    이미지 뷰는 resizable 등의 modifier 가 없으면 자신의 크기를 항상 고정시키는 측면에서 경직적입니다.
    고양이 이미지는 100x100 으로 크기가 고정되어 있고, 이미지 뷰는 이 크기를 그대로 자신의 크기로 결정합니다.

  4. 레이아웃 원칙에 따르면 자식 뷰의 사이즈는 항상 자식 뷰가 결정합니다. 
    이미지 뷰는 너비 90 을 제안받지만 자신은 100 으로 고정되어있기 때문에 자신의 너비 사이즈를 100으로 결정하고
    Stack 에 알려줍니다.

  5. Stack 은 이제 170 의 너비가 남았습니다.
    나머지 텍스트 뷰들에게 위 과정 2 ~ 4 를 반복합니다.

  6. 모든 자식 뷰의 크기가 결정되면 HStack 은 처음에 제외했던 자식 뷰 사이에 Spacing 과 함께 자식 뷰 들을 정렬합니다.
    정렬 기준이 명시되지 않았을 때는 기본적으로 가운데 정렬이 적용됩니다.
    HStack 은 VerticalAlignment.center 기준으로 정렬시키고 VStack 은 HorizontalAlignment.center 기준으로 정렬시킵니다.

  7. 이제 HStack 은 자신의 크기를 자식 뷰들을 정확히 수용할 정도로만 결정하고 
    자신의 부모 뷰에 알려줍니다. 

여기까지가 Stack 이 제안받은 크기가 충분한 경우의 레이아웃 과정입니다.

이번에는 HStack 의 부모 뷰가 충분한 크기를 제안하지 않는 경우를 살펴보겠습니다.

 

struct StackExample: View {
    var body: some View {
        
        
        VStack {
            
            HStack {
                
                Text("Cute")
                Image("cat_100x100")
                Text("baby cat")
                
            }
            .border(Color.blue)
            .lineLimit(1)
        }
        .frame(width: 200, height: 200)
        .border(Color.red)
        
    }
}

Text 안의 컨텐츠가 줄바꿈이 가능하지 않도록 하는 modifer, lineLimit 을 추가했습니다.

부모 뷰가 너비를 300 이 아닌 200 으로 제안했더니, HStack 안에 후 순위 Text 가 잘려서 ... 으로 나타났네요.

Stack 은 Text 의 컨텐츠를 다 나타낼 수 없으면 ... 으로 자르는 것을 알 수 있습니다.

이미지는 잘리지 않습니다.

 

baby cat 텍스트의 레이아웃 우선순위를 높여서 cute 가 잘리도록 만들어 보겠습니다.

Text("baby cat")
	.layoutPriority(1)

 

layoutPriority 의 기본 값은 0 이고 baby cat 텍스트의 우선순위를 1 로 높였습니다.

 

이런 경우에, Stack 은 

1 . 최우선순위 자식 뷰(들)를 제외한 다른 자식들 뷰 너비의 합을 Stack 부모 뷰에게 제안받은 너비에서 제외합니다.

2. 그리고 남은 부분 전부를 최우선순위 자식 뷰에게 제안합니다. 

 

최우선순위 자식 뷰가 자신의 사이즈를 결정한 후, 다음 우선순위 뷰들 사이에서 위에서 설명했던 과정이 

진행됩니다.

 

WWDC 에서 말한 자식 뷰들이 Stack 의 크기를 두고 경쟁한다는 말이 어느정도 이해가 되시나요?

자식 뷰들 사이에 우선순위가 존재하고 높은 우선순위 부터 자신의 크기를 결정하고 

Stack 은 이를 기반으로 자식 뷰들을 정렬, 자신의 크기도 정하니까 경쟁이라는 표현을 사용한 것 같아요.

 

Stack 이 어떻게 레이아웃 되는지 기본적으로 살펴봤습니다.

이보다 더 자세한 내용은 WWDC2019 Building Custom Views with SwiftUI 를 참고해주세요 :)

https://developer.apple.com/videos/play/wwdc2019/237

 

다음 포스트 부터는 GeometryReader, Preference, alignmentGuide 등 더 복잡한 레이아웃 개념을 다뤄보도록 하겠습니다. 

사실 여기까지는 이 개념들을 다루기 위한 준비입니다.

SwiftUI 로 정교하게 디자인을 구현하려면 위의 도구들을 반드시 이해해야 합니다. 

열심히 공부해서 정리해보도록 하겠습니다.