`@ViewBuilder`란?
`@ViewBuilder`는 SwiftUI에서 여러 뷰를 묶어 하나의 반환 값으로 처리할 수 있도록 돕는 결과 빌더(Result Builder).
이를 통해 SwiftUI에서 여러 개의 뷰를 조건적으로 반환하거나 반복적으로 생성하는 UI 코드를 간결하고 선언적으로 작성할 수 있음.
SwiftUI에서 많이 사용되는 `VStack`, `HStack`, `ZStack`과 같은 컨테이너 안에서 여러 뷰를 배치하거나, 뷰 속성을 정의할 때 사용. `@ViewBuilder`는 특히 SwiftUI가 뷰의 계층 구조를 효율적으로 관리하고 렌더링하는 데 중요한 역할.
1. `@ViewBuilder`의 역할
- 여러 뷰를 하나의 뷰로 반환
- SwiftUI에서 여러 뷰를 반환하면 이를 하나로 묶어 렌더링할 수 있도록 도움.
- 조건부 및 반복문 지원
- `if-else`, `ForEach` 등으로 동적으로 뷰를 생성할 수 있음.
- 암시적 반환
- `@ViewBuilder` 내에서는 `return` 키워드를 사용하지 않고도 값을 반환 가능.
2. 기본 사용법
struct MyView: View {
@ViewBuilder
var content: some View {
Text("첫 번째 뷰")
Text("두 번째 뷰")
}
var body: some View {
VStack {
content
}
}
}
- `@ViewBuilder`를 사용한 `content`는 여러 개의 `Text`뷰를 반환.
- `VStack` 내부에서 `content`가 호출될 때, SwiftUI는 이를 자동으로 그룹화하여 렌더링.
- `@ViewBuilder` 를 주석처리 할 경우 컴파일 에러 발생.
- 하나의 뷰만 반환해야 하는데, `Text` 두 개를 작성하면 Swift는 이를 서로 다른 반환 값으로 인식하여 단일 반환 타입 규칙을 위반.
3. `body`에서 `@ViewBuilder`를 생략할 수 있는 이유
- 위의 `content`와 같이 반환 값도 `some View`인데, `body`에서는 `Text` 두 개를 작성해도 컴파일 에러가 발생하지 않음.
SwiftUI의 `View` 프로토콜의 `body` 속성 정의
@ViewBuilder @MainActor var body: Self.Body { get }
- SwiftUI는 `body`를 정의할 때 이미 내부적으로 `@ViewBuilder`를 적용하도록 설계되어 있음.
- 따라서, `body`의 구현부에서는 별도로 `@ViewBuilder`를 선언하지 않아도 SwiftUI가 이를 자동으로 처리.
4. 조건문과 반복문 사용
`@ViewBuilder`는 조건문과 반복문을 지원하여 뷰를 동적으로 생성할 수 있음.
조건문 사용
struct ConditionalView: View {
let showGreeting: Bool
var body: some View {
VStack {
if showGreeting {
Text("안녕하세요!")
} else {
Text("안녕히 가세요!")
}
}
}
}
- `showGreeting` 값에 따라 표시되는 텍스트가 달라짐.
반복문 사용
struct ForEachView: View {
let items = ["Apple", "Banana", "Cherry"]
var body: some View {
VStack {
ForEach(items, id: \\.self) { item in
Text(item)
}
}
}
}
- `ForEach`를 통해 배열의 요소를 반복하며 뷰를 생성.
5. `@ViewBuilder`를 사용하는 위치
속성에 적용
`@ViewBuilder`는 속성에서 여러 뷰를 반환할 때 사용.
@ViewBuilder
var dynamicContent: some View {
Text("첫 번째 뷰")
Text("두 번째 뷰")
}
함수에 적용
`@ViewBuilder`는 함수에 적용하여 여러 뷰를 반환할 수 있음.
@ViewBuilder
func makeViews(showExtra: Bool) -> some View {
Text("기본 뷰")
if showExtra {
Text("추가 뷰")
}
}
- `makeViews(showExtra:)`는 조건에 따라 추가 뷰를 반환.
커스텀 뷰에서 사용
커스텀 컨테이너 뷰를 만들 때, `@ViewBuilder`를 활용하여 내부 뷰를 동적으로 구성할 수 있음.
struct CustomContainer<Content: View>: View {
let color: Color
let content: Content
init(
color: Color,
@ViewBuilder content: () -> Content
) {
self.color = color
self.content = content()
}
var body: some View {
VStack {
content
}
.foregroundStyle(color)
}
}
struct ContentView: View {
var body: some View {
CustomContainer(color: .blue) {
Text("First Text")
Text("Second Text")
}
Text("Third Text")
}
}
6. 작동 방식: 내부적으로 어떤 일이 일어나는가?
`@ViewBuilder`는 여러 개의 뷰를 반환하면 이를 묶어 SwiftUI에서 처리할 수 있도록 도움.
반환된 뷰는 SwiftUI 내부적으로 `TupleView`나 `Group`으로 변환.
단일 뷰 반환
- 하나의 뷰만 반환하면 그대로 렌더링.
@ViewBuilder
var singleView: some View {
Text("단일 뷰")
}
// 단일 Text 뷰 반환
다중 뷰 반환
- 여러 뷰를 반환하면 `TupleView`로 묶임.
@ViewBuilder
var multipleViews: some View {
Text("첫 번째")
Text("두 번째")
}
// 내부적으로 TupleView((Text("첫 번째"), Text("두 번째")))
7. 제약 사항 및 주의점
최대 10개 제한
- `TupleView`는 최대 10개의 뷰만 포함할 수 있음.
- 이를 초과하면 `Group`으로 묶어야 함.
@ViewBuilder
var manyViews: some View {
Group {
Text("1")
Text("2")
Text("3")
Text("4")
Text("5")
Text("6")
Text("7")
Text("8")
Text("9")
Text("10")
Text("11") // Group으로 묶지 않으면 오류 발생
}
}
조건문 누락
- 조건문에 따라 반환 값이 없는 경우 SwiftUI가 처리할 수 없으므로, 모든 분기에서 뷰를 반환해야 함.
@ViewBuilder
var invalidView: some View {
if true {
Text("조건에 맞는 뷰")
}
// else 분기에서 반환 값 없음 -> 오류 발생
}
8. 정리
`@ViewBuilder`는 SwiftUI에서 동적이고 선언적인 UI를 구축하는 데 필수적인 도구.
조건에 따라 동적으로 뷰를 생성하거나, 반복문을 사용해 많은 뷰를 간결하게 구성할 수 있음.
특징 | 설명 |
뷰 그룹화 | 여러 뷰를 하나의 반환 값으로 묶어 렌더링 가능 |
조건 및 반복 지원 | `if-else`, `ForEach` 등으로 동적 뷰 생성 가능 |
적용 위치 | 속성, 함수, 커스텀 컨테이너 뷰 등에 적용 가능 |
제약 | `TupleView`로 묶일 경우 최대 10개 제한 |
`@ViewBuilder`를 적절히 활용하면 SwiftUI의 선언적 문법을 최대한 활용하여 가독성 높고 유지 보수성이 좋은 코드를 작성할 수 있음.
'SwiftUI' 카테고리의 다른 글
[SwiftUI] @AppStorage (0) | 2024.05.23 |
---|---|
[SwiftUI] @ObservableObject / @Published / @StateObject / @ObservedObject (0) | 2024.05.20 |
[SwiftUI] AsyncImage in SwiftUI (0) | 2024.05.13 |
[SwiftUI] ForEach in SwiftUI (0) | 2024.05.13 |
[SwiftUI] NavigationView vs NavigationStack (0) | 2024.05.09 |