SwiftUI

[SwiftUI] @ViewBuilder

yujaehui 2024. 5. 20. 12:23

`@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의 선언적 문법을 최대한 활용하여 가독성 높고 유지 보수성이 좋은 코드를 작성할 수 있음.