SwiftUI 스택 정렬
기본적인 스택 정렬에 대해서는 VStack와 HStack에 대해서는 다뤄봤지만 복잡한 레이아웃을 설계 할 때는 표준 정렬 방법 이상의 방법이 필요하다
컨테이너 정렬
swiftUI스택을 사용할때 사용할 수 있는 가장 기본적인 방법이 컨테이너 정렬이다 스택에 포함된 하위 뷰들이 스택 내에서 정렬되는 방식을 의미한다 스택에 포함된 뷰에 지정된 정렬이 없다면 스택에 적용한 정렬 방법이 적용되는데 이를 암묵적 정렬이라고 한다
1
2
3
4
5
6
7
8
9
10
struct ContentView: View {
var body: some View {
VStack(alignment: .trailing) {
Text("Hello, world!")
Text("dddfa")
Text("dddfa12312sdfasf")
}
.padding()
}
}
사진과 같이 하위 뷰에 지정된 정렬이 없어서 스택에 지정된 trailing 오른쪽 정렬이 되었다 ( 기본값은 중앙정렬)
수평정렬에서는 텍스트 베이스 라인 정렬을 할 수 있다
1
2
3
4
5
6
7
8
9
10
11
12
struct ContentView: View {
var body: some View {
HStack(alignment: .firstTextBaseline) {
Text("Hello, world!")
.font(.largeTitle)
Text("dddfa")
Text("dddfa12312sdfasf")
.font(.title)
}
.padding()
}
}
텍스트 베이스 라인 정렬은 텍스트 기반 첫줄 or 마지막 줄 기준으로 할 수 있다
정렬 가이드
alignment guide는 뷰가 스택에 포함된 다른 뷰와 정렬해야 할 때 사용되는 커스텀 포지션을 정의하는데 사용된다 이는 복잡한 정렬을 구현할 수 있게 해준다
정렬가이드는 alignmentGuide()수정자를 사용하여 뷰에 적용된다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import SwiftUI
struct ContentView: View {
var body: some View {
VStack(alignment: .leading) {
Rectangle()
.foregroundStyle(Color.green)
.frame(width: 120, height: 50)
Rectangle()
.foregroundStyle(Color.blue)
.frame(width: 200, height: 50)
Rectangle()
.foregroundStyle(Color.red)
.frame(width: 160, height: 50)
}
.padding()
}
}
위 사진처럼 랜더링된다 여기서 블루만 왼쪽으로 120포인트 이동할려면 정렬 가이드를 이용한다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import SwiftUI
struct ContentView: View {
var body: some View {
VStack(alignment: .leading) {
Rectangle()
.foregroundStyle(Color.green)
.frame(width: 120, height: 50)
Rectangle()
.foregroundStyle(Color.blue)
.alignmentGuide(.leading, computeValue: { d in 120.0 })
.frame(width: 200, height: 50)
Rectangle()
.foregroundStyle(Color.red)
.frame(width: 160, height: 50)
}
.padding()
}
}
초록색 사각형과 세번째 사각형은 여전히 앞쪽 정렬을 유지하지만 파란색 사각형은 120 포인트 왼쪽에서 정렬되었다 그리고 정렬가이드를 사용할때 부모 스택에 적용된 정렬타입과 일치해야한다 VStack(alignment: .leading) 와 .alignmentGuide(.leading)
또한 120 이렇게 하드 코딩하지 않고 width 프로퍼티를 이용해서 뷰의 크키에 맞춰 조정 가능하다
1
2
3
4
Rectangle()
.foregroundStyle(Color.blue)
.alignmentGuide(.leading, computeValue: { d in d.width / 3 })
.frame(width: 200, height: 50)
또한 computeValue에서 HorizontalAligment와 VerticalAlignment 프로퍼티 접근이 가능하다
1
2
3
4
Rectangle()
.foregroundStyle(Color.blue)
.alignmentGuide(.leading, computeValue: { d in d[HorizontalAlignment.trailing] + 20 }) // 뷰 끝에 20 포인트
.frame(width: 200, height: 50)
커스텀 정렬
여러 뷰에 적용할 수 있는 커스텀 정렬을 생성한다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import SwiftUI
struct ContentView: View {
var body: some View {
HStack(alignment: .oneThird) {
Rectangle()
.foregroundStyle(Color.green)
.frame(width: 50, height: 200)
Rectangle()
.foregroundStyle(Color.red)
.alignmentGuide(.oneThird, computeValue: {d in d[VerticalAlignment.top]})
.frame(width: 50, height: 200)
Rectangle()
.foregroundStyle(Color.blue)
.frame(width: 50, height: 200)
Rectangle()
.foregroundStyle(Color.yellow)
.alignmentGuide(.oneThird, computeValue: {d in d[VerticalAlignment.top]})
.frame(width: 50, height: 200)
}
.padding()
}
}
extension VerticalAlignment{
private enum OneThird: AlignmentID {
static func defaultValue(in d: ViewDimensions) -> CGFloat {
return d.height / 3
}
}
static let oneThird = VerticalAlignment(OneThird.self)
}
VerticalAligment를 확장해서 생성한것으로 이 extension은 AlignmentID 프로토콜을 따르는 열거형을 포함해야 하며 defaultValue()라는 이름의 함수가 구현되야한다 CGFloat 값을 반환하고 여기서는 현재 뷰 높이의 1/3이 반환된다
표준 스택 정렬 커스텀 이용
사용자는 표준 스택(HStack, VStack)을 복합적으로 사용해서 뷰를 만들수 있다 이 예시에서는 중첩된 스택에서 표현이 불가능한것을 커스텀 정렬을 이용해서 정렬한다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import SwiftUI
struct ContentView: View {
var body: some View {
HStack(spacing:20) {
Circle()
.foregroundStyle(Color.purple)
.frame(width: 100, height: 100)
VStack(alignment: .center) {
Rectangle()
.foregroundStyle(Color.green)
.frame(width: 100, height: 100)
Rectangle()
.foregroundStyle(Color.red)
.frame(width: 100, height: 100)
Rectangle()
.foregroundStyle(Color.blue)
.frame(width: 100, height: 100)
Rectangle()
.foregroundStyle(Color.yellow)
.frame(width: 100, height: 100)
}
}
.padding()
}
}
이 화면 상태에서는 보라색 원을 초록색과 정렬하고 싶다면 HStack정렬을 .top 노란색과 정렬하고 싶다면 .bottom 을 이용하면 된다 하지만 빨간색이나 파란색에 정렬을 하고 싶다면 기본적인 방법으로는 불가능하다
커스텀 정렬 생성
1
2
3
4
5
6
7
8
extension VerticalAlignment{
private enum CrossAligment: AlignmentID {
static func defaultValue(in d: ViewDimensions) -> CGFloat {
return d[.bottom] // top으로 바꿔도 상관없었음
}
}
static let crossAlignment = VerticalAlignment(CrossAligment.self)
}
이 커스텀 정렬을 Circle()과 3번째 Rectangle()에 적용한다면
위 화면처럼 3번째와 같이 정렬 두번째 Rectangle()에 적용한다면 원이 빨간색 사각형 옆에 정렬된다