#### SwiftUI 스택 SwiftUI는 VStack, HStack, ZStack 형태인 3개의 스택 레이아웃 뷰를 제공한다. 스택은 SwiftUI View 파일 내에 하위 뷰들이 스택 뷰에 포함되도록 선언된다.
- 이를 이용해 상당히 복잡한 레이아웃도 간단하게 설계할 수 있다.
Spacer, alignment, padding
SwiftUI는 뷰 사이에 공간을 추가하기 위한 Spacer 컴포넌트를 가지고 있다. 이를 스택 안에서 사용하면 Spacer는 배치된 뷰들의 간격을 제공하기 위해 스택의 방향에 따라 유연하게 확장 / 축소된다.
스택의 정렬, 간격은 스택이 선언될 때 정렬 값을 지정하면 된다.
VStack(alignment: .center, spacing: 15) { Text("Hello") .font(.title) } - 뷰 주변의 간격은 padding() 수정자를 이용해 호출할 수 있다. 매개변수 없이 호출하면 SwiftUI는 레이아웃, 콘텐트, 화면 크기에 대한 최적의 간격을 자동으로 사용한다.
컨테이너의 자식 뷰 제한
컨테이너 뷰는 직접적인 하위 뷰를 10개로 제한한다. 만약 스택 컨테이너가 10개 이상의 자식 뷰를 담으면 구문 오휴가 표시된다.
만약 스택에 포함된 직접적인 자식 뷰가 10개를 넘어야 한다면 뷰들은 여러 컨테이너로 나눠서 담겨야 할 것이다. 물론, 하위 뷰로 스택을 추가하여 할 수도 있지만 Group 뷰라는 또 다른 유용한 컨테이너가 있다. 다음 예제는 Text 뷰가 Group 컨테이너로 나눠져서 VStack에는 단 2개의 직접적인 자식 뷰만 포함하게 한다.
VStack { Group { Text("Sample") Text("Sample") Text("Sample") Text("Sample") Text("Sample") } Group { Text("Sample") Text("Sample") Text("Sample") Text("Sample") Text("Sample") } } - 이것은 10개의 하위 뷰 제한을 피하게 할 뿐만 아니라, Group은 여러 뷰에서 작업을 수행할 때에도 유용하다. 예를 들어, Group에 뷰들을 포함시키고 숨기는 명령을 주면 하나의 명령으로 모든 뷰를 숨길 수 있다.
동적 HStack과 VStack 변환
설계 단계에서 HStack으로 할 지 아니면 VStack으로 할 지에 대한 결정은 마지막에 해야 한다. SwiftUI는 레이아웃에 사용되는 스택 타입을 앱 코드 내에서 동적으로 변경할 수도 있게 한다.
이것은 AnyLayout 인스턴스를 생성하고 HStackLayout 또는 VStackLayout 컨테이너에 할당하여 할 수 있다.
@State var myLayout: AnyLayout = AnyLayout(VStackLayout()) var body: some View { VStack { myLayOut { Image(systemName: "goforward.10") .resizable() .aspectRatio(contentMode: .fit) Image(systemName: "goforward.10") .resizable() .aspectRatio(contentMode: .fit) } HStack { Button(action: { myLayout = AnyLayout(HStackLayout()) }){ Text("HStack") } Button(action: { myLayout = AnyLayout(VStackLayout()) }){ Text("VStack") } } } } 텍스트 줄 제한과 레이아웃 우선순위
스택의 크기가 제한되어 있거나 다른 뷰들과 공간을 나눠서 사용해야 하는 상황처럼, 스택이 충분한 공간을 가지고 있지 않다면 텍스트는 자동으로 여러 줄로 표시될 것이다.
- 텍스트를 몇 줄로 표현할 지는 lineLimit() 수정자를 사용하면 정할 수 있다.
HStack { Image(systemName: "airplane") Text("Flight times:") Text("London") } .font(.largeTitle) .lineLimit(1) // 최대 한 줄까지 사용 가능 - 줄 제한은 텍스트가 표시될 수 있는 최대 및 최소 줄 수를 제공하는 범위로 지정할 수도 있다.
HStack { Image(systemName: "airplane") Text("Flight times:") Text("London") } .lineLimit(1...4) - 이렇게 지정 후에 공간이 부족하면 텍스트가 잘려서 표현될 것이다.
우선순위에 대한 가이드가 없으면 스택 뷰는 안에 포함된 뷰들의 길이와 여유 공간을 기반으로 Text 뷰를 어떻게 자를 지 결정하게 된다.
HStack { Image(systemName: "airplane") Text("Flight times:") Text("London") .layoutPriority(1) } .lineLimit(1...4) 전통적 스택 VS 지연 스택
지연 스택(lazy)은 도달시에 생성된다. 그렇기에 시스템 리소스를 적게 먹는다. 기존 스택을 사용할 지 아니면 지연 스택을 사용할 지에 대해 결정할 때는 일반적으로 기존 스택을 사용하여 시작하고 많은 수의 자식 뷰로 인한 성능 문제가 발생할 때 지연 스택으로 전환하는 것이 좋다.
SwiftUI 프레임
프레임 수정자를 통해 다양한 레이아웃 다루기
여러 수정자가 연결되면 뷰의 모양에 영향을 미치곤 한다는 점을 기억하자. 예제 코드에서 사용 가능한 영역의 경계선을 그리고자 한다면 다음처럼 코드를 쓸 수 있다.
Text("Hello World, how are you?") .font(.largeTitle) .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity) .border(Color.black, width: 5) - 디폴트로, frame은 화면을 채울 때 화면의 안전 영역을 준수한다.
- 안전 영역 밖까지 확장되도록 frame을 구성하려면 edgesIgnoringSafeArea() 수정자를 사용하면 안전 영역을 무시하게 된다.
.edgesIgnoringSafeArea(.all) frame과 GeometryReader
프레임은 뷰들을 담고 있는 컨테이너의 크기에 따라 조절되도록 구현할 수도 있다. 이 작업은 GeometryReader로 뷰를 감싸하고 컨테이너의 크기를 식별하기 위한 리더를 이용하여 할 수 있다.
이 리더는 프레임의 크기를 계산하는 데 사용된다. 다음의 예제는 두 개의 Text뷰를 포함하고 있는 VStack의 크기를 기준으로 Text 뷰의 크기를 설정한다.
GeometryReader { geometry in VStack { Text("Hello World, how are you?") .font(largeTitle) .frame(width: geometry.size.width / 2, height: (geometry.size.height / 4) * 3) Text("Goodbye World") .font(.largeTitle) .frame(width: geometry.size.width / 3, height: geometry.size.height / 4) } } - 상단에 있는 Text 뷰는 VStack의 1/2의 폭과 3/4의 높이를 차지하고, 하단에 있는 Text View는 VStack의 1/3의 폭과 1/4의 높이를 차지하기 위한 설정이다.
전체적인 그림
- GeometryReader가
요약
사용자 인터페이스 설계는 컴포넌트들을 가져다가 직관적인 사용자 경험을 제공하도록 화면에 배치하는 것이다. UI 레이아웃은 화면 크기, 디바이스의 방향에 관계없이 모든 디바이스에서 올바르게 표시되도록 해야 한다. 이를 위해 SwiftUI는 몇 가지 레이아웃 뷰와 컴포넌트를 제공한다.
기본적으로, 뷰는 콘텐트와 뷰를 포함하고 있는 컨테이너에 적용된 제한에 따라 크기가 정해진다. 뷰가 사용할 수 있는 공간이 충분하지 않은 경우 뷰의 크기가 제한적이므로 콘텐트가 잘리게 된다.
우선순위 설정을 하면 컨테이너 안의 다른 뷰의 크기보다 줄이거나 늘릴 수 있다.
뷰에 할당된 공간을 보다 효과적으로 제어하기 위해 뷰에 유연한 프레임을 적용할 수 있다. 프레임은 크기를 고정하거나, 최솟값과 최댓값 범위 내에서 제한하거나, GeometryReader를 사용하여 뷰 크기를 조절할 수 있다.