Post

SwiftUI 스택 정렬

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()
    }
}
firstTextBaseline 모든 뷰의 첫 번째 텍스트 줄의 기준선을 따라 정렬 예를 들어, 여러 텍스트 뷰가 포함된 경우, 각 뷰의 첫 번째 줄이 동일한 높이에 맞춰지도록 정렬
lastTextBaseline 각 뷰의 마지막 텍스트 줄의 기준선을 따라 정렬 여러 텍스트 뷰가 있을 때, 각 뷰의 마지막 줄이 동일한 높이에 맞춤

텍스트 베이스 라인 정렬은 텍스트 기반 첫줄 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에서 HorizontalAligmentVerticalAlignment 프로퍼티 접근이 가능하다

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()에 적용한다면 원이 빨간색 사각형 옆에 정렬된다

This post is licensed under CC BY 4.0 by the author.