지난 포스팅에서는 SwiftUI의 기본 스택인 VStack, HStack, ZStack에 대해 알아봤다.
이러한 스택과 함께 자주 사용되는 세 가지 중요한 레이아웃 도구인 Spacer, Padding, Frame에 대해 자세히 알아보겠다.
Spacer 사용하기

Spacer는 사용 가능한 공간을 모두 채우는 특별한 뷰다. 뷰 사이에 유연한 공간을 만들거나 특정 뷰를 화면의 한쪽으로 밀어내는 데 사용된다.
Spacer의 기본 원리
Spacer는 자신이 속한 스택 내에서 가능한 모든 공간을 차지한다. 여러 개의 Spacer가 있다면 공간을 균등하게 나눠 갖는다.
// 기본 Spacer 사용법
Spacer()
// 최소 크기를 지정한 Spacer
Spacer(minLength: 20) // 최소 20포인트 공간 확보
Spacer 활용 예제
1. 뷰를 양 끝으로 밀어내기
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 사용하기
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. 뷰를 화면 하단으로 밀기
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 사용하기

Padding은 뷰 주변에 여백을 추가한다. 여백은 UI 요소 간의 간격을 만들고 시각적으로 보기 좋은 레이아웃을 만드는 데 중요하다.
Padding의 기본 시그니처
// 기본 패딩 (모든 방향에 기본값 적용)
.padding()
// 특정 방향에만 패딩 적용
.padding(.top, 20)
.padding(.horizontal, 15)
// 모든 방향에 다른 값 적용
.padding(EdgeInsets(top: 10, leading: 15, bottom: 10, trailing: 15))
Padding 활용 예제
1. 기본 패딩
struct PaddingExample1: View {
var body: some View {
Text("기본 패딩")
.padding() // 모든 방향에 기본 패딩 적용
.background(Color.yellow)
// vs 패딩 없음
Text("패딩 없음")
.background(Color.orange)
}
}
2. 방향별 패딩
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. 중첩 패딩
struct PaddingExample3: View {
var body: some View {
VStack {
Text("중첩 패딩 예시")
.foregroundColor(.white)
.padding() // 내부 패딩
.background(Color.blue)
.padding() // 외부 패딩
.background(Color.red)
.cornerRadius(10)
}
}
}
Frame 사용하기

Frame은 뷰의 크기와 정렬을 제어한다. 고정 크기를 지정하거나, 최소/최대 크기 제한을 설정할 수 있다.
Frame의 기본 시그니처
// 고정 크기
.frame(width: 100, height: 50)
// 최대 크기 지정 (유연하게)
.frame(maxWidth: .infinity, maxHeight: 200)
// 최소 크기 지정
.frame(minWidth: 100, minHeight: 100)
// 정렬 지정
.frame(width: 200, height: 100, alignment: .topLeading)
Frame 활용 예제
1. 고정 크기 프레임
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. 유연한 프레임
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. 정렬이 있는 프레임
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()
}
}

세 가지 조합하기
실제 앱 개발에서는 Spacer, Padding, Frame을 조합하여 사용하는 경우가 많다. 이 세 가지를 적절히 조합하면 거의 모든 레이아웃을 구현할 수 있다.
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()
: 프레임을 먼저 지정한 후 외부에 패딩 적용
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
를 사용하면 가능한 최대 공간을 차지하도록 할 수 있다. 특히 버튼이나 헤더 등을 화면 너비에 맞추고 싶을 때 유용하다.
Button("전체 너비 버튼") {
// 액션
}
.padding()
.frame(maxWidth: .infinity)
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
레이아웃 디버깅
레이아웃 문제를 디버깅할 때는 임시로 background 색상을 지정하거나 border를 추가하면 각 요소의 경계를 시각적으로 확인할 수 있다.
Text("디버깅 중")
.padding()
.background(Color.yellow) // 배경 색상으로 확인
.border(Color.red) // 테두리로 확인
Spacer, Padding, Frame 사용법을 마치며
SwiftUI에서 Spacer, Padding, Frame은 레이아웃을 구성하는 필수적인 도구들이다. 이 세 가지를 마스터하면 복잡한 UI도 쉽게 구현할 수 있다.
- Spacer: 공간을 유연하게 채워 뷰를 원하는 위치로 밀어낸다
- Padding: 뷰 주변에 여백을 추가하여 시각적으로 더 보기 좋게 만든다
- Frame: 뷰의 크기와 정렬 방식을 제어한다
이러한 도구들을 적절히 조합하여 사용하면 다양한 기기의 화면 크기에 자동으로 적응하는 반응형 UI를 구현할 수 있다.
특히 SwiftUI의 선언적 구문을 통해 이러한 레이아웃 요소들을 직관적으로 조합할 수 있어, 빠르게 UI를 프로토타이핑하고 개발하는 데 큰 도움이 된다.
더 자세한 내용은 Apple 공식 문서에서 확인할 수 있다.