SwiftUI 기초: Spacer, Padding, Frame 사용 방법

지난 포스팅에서는 SwiftUI의 기본 스택인 VStack, HStack, ZStack에 대해 알아봤다.

이러한 스택과 함께 자주 사용되는 세 가지 중요한 레이아웃 도구인 Spacer, Padding, Frame에 대해 자세히 알아보겠다.

Spacer 사용하기


SwiftUI Spacer
SwiftUI Spacer

Spacer는 사용 가능한 공간을 모두 채우는 특별한 뷰다. 뷰 사이에 유연한 공간을 만들거나 특정 뷰를 화면의 한쪽으로 밀어내는 데 사용된다.

Spacer의 기본 원리

Spacer는 자신이 속한 스택 내에서 가능한 모든 공간을 차지한다. 여러 개의 Spacer가 있다면 공간을 균등하게 나눠 갖는다.

Swift
// 기본 Spacer 사용법
Spacer()

// 최소 크기를 지정한 Spacer
Spacer(minLength: 20) // 최소 20포인트 공간 확보

Spacer 활용 예제

1. 뷰를 양 끝으로 밀어내기

Swift
struct SpacerExample1: View {
    var body: some View {
        HStack {
            Text("왼쪽")
                .padding()
                .background(Color.blue.opacity(0.3))
            
            Spacer() // 가운데 공간을 모두 차지
            
            Text("오른쪽")
                .padding()
                .background(Color.green.opacity(0.3))
        }
        .padding()
        .background(Color.gray.opacity(0.2))
        .cornerRadius(10)
    }
}

2. 여러 Spacer 사용하기

Swift
struct SpacerExample2: View {
    var body: some View {
        HStack {
            Text("왼쪽")
            
            Spacer() // 첫 번째 Spacer
            
            Text("중앙")
            
            Spacer() // 두 번째 Spacer
            
            Text("오른쪽")
        }
        .padding()
        .background(Color.yellow.opacity(0.2))
        .cornerRadius(10)
        // 두 Spacer가 공간을 균등하게 나눠 세 텍스트를 동일한 간격으로 배치
    }
}

3. 뷰를 화면 하단으로 밀기

Swift
struct SpacerExample3: View {
    var body: some View {
        VStack {
            Text("상단 내용")
                .font(.title)
            
            // VStack의 남은 공간을 모두 차지하여 하단 버튼을 아래로 밀어냄
            Spacer()
            
            Button("확인") {
                // 버튼 액션
            }
            .padding()
            .frame(maxWidth: .infinity)
            .background(Color.blue)
            .foregroundColor(.white)
            .cornerRadius(10)
        }
        .padding()
        .frame(height: 400) // 예시용 고정 높이
        .background(Color.gray.opacity(0.1))
        .cornerRadius(12)
    }
}

Padding 사용하기


SwiftUI Padding
SwiftUI Padding

Padding은 뷰 주변에 여백을 추가한다. 여백은 UI 요소 간의 간격을 만들고 시각적으로 보기 좋은 레이아웃을 만드는 데 중요하다.

Padding의 기본 시그니처

Swift
// 기본 패딩 (모든 방향에 기본값 적용)
.padding()

// 특정 방향에만 패딩 적용
.padding(.top, 20)
.padding(.horizontal, 15)

// 모든 방향에 다른 값 적용
.padding(EdgeInsets(top: 10, leading: 15, bottom: 10, trailing: 15))

Padding 활용 예제

1. 기본 패딩

Swift
struct PaddingExample1: View {
    var body: some View {
        Text("기본 패딩")
            .padding() // 모든 방향에 기본 패딩 적용
            .background(Color.yellow)
            
            // vs 패딩 없음
        Text("패딩 없음")
            .background(Color.orange)
    }
}

2. 방향별 패딩

Swift
struct PaddingExample2: View {
    var body: some View {
        VStack(spacing: 20) {
            Text("상단 패딩")
                .padding(.top, 30) // 상단만 30포인트
                .background(Color.blue.opacity(0.3))
                .border(Color.blue)
            
            Text("수평 패딩")
                .padding(.horizontal, 50) // 좌우만 50포인트
                .background(Color.green.opacity(0.3))
                .border(Color.green)
            
            Text("하단 패딩")
                .padding(.bottom, 20) // 하단만 20포인트
                .background(Color.red.opacity(0.3))
                .border(Color.red)
            
            Text("모든 방향 다른 값")
                .padding(EdgeInsets(top: 10, leading: 30, bottom: 20, trailing: 5))
                .background(Color.purple.opacity(0.3))
                .border(Color.purple)
        }
    }
}

3. 중첩 패딩

Swift
struct PaddingExample3: View {
    var body: some View {
        VStack {
            Text("중첩 패딩 예시")
                .foregroundColor(.white)
                .padding() // 내부 패딩
                .background(Color.blue)
                .padding() // 외부 패딩
                .background(Color.red)
                .cornerRadius(10)
        }
    }
}

Frame 사용하기


SwiftUI Frame
SwiftUI Frame

Frame은 뷰의 크기와 정렬을 제어한다. 고정 크기를 지정하거나, 최소/최대 크기 제한을 설정할 수 있다.

Frame의 기본 시그니처

Swift
// 고정 크기
.frame(width: 100, height: 50)

// 최대 크기 지정 (유연하게)
.frame(maxWidth: .infinity, maxHeight: 200)

// 최소 크기 지정
.frame(minWidth: 100, minHeight: 100)

// 정렬 지정
.frame(width: 200, height: 100, alignment: .topLeading)

Frame 활용 예제

1. 고정 크기 프레임

Swift
struct FrameExample1: View {
    var body: some View {
        VStack(spacing: 20) {
            Text("고정 크기 프레임")
                .frame(width: 200, height: 60)
                .background(Color.blue.opacity(0.3))
                .border(Color.blue)
            
            Text("내용이 매우 길어도 지정된 크기를 유지하는 프레임입니다")
                .frame(width: 150, height: 100)
                .background(Color.green.opacity(0.3))
                .border(Color.green)
        }
    }
}

2. 유연한 프레임

Swift
struct FrameExample2: View {
    var body: some View {
        VStack(spacing: 20) {
            Text("최소 너비가 있는 프레임")
                .frame(minWidth: 200)
                .background(Color.yellow.opacity(0.3))
                .border(Color.yellow)
            
            Text("최대 너비가 있는 긴 텍스트입니다. 너무 길어지면 최대 너비 제한에 맞게 줄바꿈됩니다.")
                .frame(maxWidth: 200)
                .background(Color.orange.opacity(0.3))
                .border(Color.orange)
            
            Text("무한대 너비")
                .frame(maxWidth: .infinity)
                .background(Color.red.opacity(0.3))
                .border(Color.red)
        }
        .padding()
    }
}

3. 정렬이 있는 프레임

Swift
struct FrameExample3: View {
    var body: some View {
        VStack(spacing: 20) {
            Text("왼쪽 위 정렬")
                .frame(width: 200, height: 100, alignment: .topLeading)
                .background(Color.purple.opacity(0.3))
                .border(Color.purple)
            
            Text("오른쪽 아래 정렬")
                .frame(width: 200, height: 100, alignment: .bottomTrailing)
                .background(Color.blue.opacity(0.3))
                .border(Color.blue)
            
            Text("중앙 정렬")
                .frame(width: 200, height: 100, alignment: .center)
                .background(Color.green.opacity(0.3))
                .border(Color.green)
        }
        .padding()
    }
}
Alignment
Alignment

세 가지 조합하기


실제 앱 개발에서는 Spacer, Padding, Frame을 조합하여 사용하는 경우가 많다. 이 세 가지를 적절히 조합하면 거의 모든 레이아웃을 구현할 수 있다.

Swift
struct CombinedExample: View {
    var body: some View {
        VStack(spacing: 0) { // 간격 없이 붙이기
            // 상단 헤더
            HStack {
                Text("할 일 목록")
                    .font(.headline)
                
                Spacer() // 오른쪽으로 밀어냄
                
                Text("4월 21일")
                    .font(.subheadline)
                    .foregroundColor(.gray)
            }
            .padding() // 전체 패딩
            .background(Color.yellow.opacity(0.2))
            
            // 중간 콘텐츠
            VStack(alignment: .leading, spacing: 12) {
                TaskRow(title: "SwiftUI 공부하기", isDone: true)
                TaskRow(title: "앱 디자인 만들기", isDone: false)
                TaskRow(title: "코드 리팩토링", isDone: false)
                
                Spacer() // 아래쪽 빈 공간 채우기
            }
            .padding() // 패딩
            .frame(maxWidth: .infinity, maxHeight: .infinity) // 최대 크기로 확장
            .background(Color.white)
            
            // 하단 버튼
            Button("새 할 일 추가") {
                // 액션
            }
            .padding() // 버튼 내부 패딩
            .frame(maxWidth: .infinity) // 너비 최대로
            .background(Color.blue)
            .foregroundColor(.white)
        }
        .frame(height: 400) // 예시 목적의 고정 높이
        .background(Color.gray.opacity(0.1))
        .cornerRadius(12)
    }
}

// 할 일 항목을 위한 보조 뷰
struct TaskRow: View {
    let title: String
    let isDone: Bool
    
    var body: some View {
        HStack {
            Image(systemName: isDone ? "checkmark.circle.fill" : "circle")
                .foregroundColor(isDone ? .green : .gray)
            
            Text(title)
                .strikethrough(isDone) // 완료시 취소선
            
            Spacer()
        }
        .padding(.vertical, 8) // 수직 패딩
        .padding(.horizontal) // 수평 패딩
        .background(Color.gray.opacity(0.05))
        .cornerRadius(8)
    }
}

여러가지 팁


패딩과 프레임의 순서

패딩과 프레임 수정자의 적용 순서에 따라 결과가 달라질 수 있다.

  • .padding().frame(): 패딩을 먼저 적용한 후 프레임 지정
  • .frame().padding(): 프레임을 먼저 지정한 후 외부에 패딩 적용
Swift
struct OrderExample: View {
    var body: some View {
        VStack(spacing: 20) {
            // 패딩 먼저, 프레임 나중
            Text("패딩 -> 프레임")
                .padding()
                .frame(width: 200, height: 60)
                .background(Color.blue.opacity(0.3))
                .border(Color.blue)
            
            // 프레임 먼저, 패딩 나중
            Text("프레임 -> 패딩")
                .frame(width: 200, height: 60)
                .padding()
                .background(Color.green.opacity(0.3))
                .border(Color.green)
        }
    }
}

.infinity의 활용

.infinity를 사용하면 가능한 최대 공간을 차지하도록 할 수 있다. 특히 버튼이나 헤더 등을 화면 너비에 맞추고 싶을 때 유용하다.

Swift
Button("전체 너비 버튼") {
    // 액션
}
.padding()
.frame(maxWidth: .infinity)
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)

레이아웃 디버깅

레이아웃 문제를 디버깅할 때는 임시로 background 색상을 지정하거나 border를 추가하면 각 요소의 경계를 시각적으로 확인할 수 있다.

Swift
Text("디버깅 중")
    .padding()
    .background(Color.yellow) // 배경 색상으로 확인
    .border(Color.red) // 테두리로 확인

Spacer, Padding, Frame 사용법을 마치며


SwiftUI에서 Spacer, Padding, Frame은 레이아웃을 구성하는 필수적인 도구들이다. 이 세 가지를 마스터하면 복잡한 UI도 쉽게 구현할 수 있다.

  • Spacer: 공간을 유연하게 채워 뷰를 원하는 위치로 밀어낸다
  • Padding: 뷰 주변에 여백을 추가하여 시각적으로 더 보기 좋게 만든다
  • Frame: 뷰의 크기와 정렬 방식을 제어한다

이러한 도구들을 적절히 조합하여 사용하면 다양한 기기의 화면 크기에 자동으로 적응하는 반응형 UI를 구현할 수 있다.

특히 SwiftUI의 선언적 구문을 통해 이러한 레이아웃 요소들을 직관적으로 조합할 수 있어, 빠르게 UI를 프로토타이핑하고 개발하는 데 큰 도움이 된다.

더 자세한 내용은 Apple 공식 문서에서 확인할 수 있다.