-
[SwiftUI] - @ViewBuilderiOS/SWIFTUI 2024. 7. 16. 22:29728x90반응형
오랜만이다.
요즘 프로젝트를 하면서 뷰 관련코드를 최적화 하려고 노력중이다.
뷰의 재사용성을 높이기 위해 연구를 하던 중 @ViewBuilder라는 키워드를 알게 되었다.
사실 우리도 모르게 우리는 ViewBuilder를 사용하고 있었는데, 과연 이 녀석은 무슨 녀석일까?
@ViewBuilder

일반적으로 ViewBuilder는 closure를 통해 여러 자식 뷰를 제공할때 사용할 수 있다. 예를 들자면, contextMenu 함수는 ViewBuilder를 통해 하나 이상의 뷰를 생성하는 closure를 허용한다.
@ViewBuilder는 SwiftUI에서 사용되는 특수한 속성 래퍼(attribute wrapper)로, 여러 뷰를 하나의 클로저로 묶어서 반환할 수 있게 해주며 이를 여러 개의 뷰를 조합해 하나의 단일 뷰처럼 사용할 수 있도록 한다.
위는 @ViewBuilder의 정의들이다. 쉽게 말해 그냥 여러 뷰를 클로져로 반환한다는 말인데, 우리가 사용하는 V/H/ZStack, ForEach 등 클로져로 안에 뷰를 담을 수 있는 컴포넌트들은 모두 이 ViewBuilder 속성으로 선언 되어있다.

그럼 Button도 클로져로 사용할 수 있는데 이것도 ViewBuilder속성으로 선언될까?
결론부터 얘기하면 맞기도 하고 아니기도 하다.
Button() 컴포넌트는 다음과 같이 초기화 되어있다.

@ViewBuilder 속성은 클로져에 뷰를 정의할 수 있도록 해주는 속성이다. label 파라미터의 경우 개발자가 직접 뷰를 정의할 수 있지만, action의 경우 뷰를 파라미터로 받는 것이 아닌 명령어나 메서드 등을 파라미터로 받는다.
이쯤 되면 이해가 대충갈 것이다.
그럼 예제를 만들어 보자
struct RainbowTextForEach<Content>: View where Content: View { let content: () -> Content let rainbow:[Color] = [.red,.orange,.yellow,.green,.blue,.indigo,.purple] init(@ViewBuilder content: @escaping () -> Content) { self.content = content } var body: some View { ForEach(rainbow,id: \.self){ color in content() .foregroundColor(color) } } }클로져에 뷰 코드를 삽입하면 그 뷰들을 모두 무지개 색 아이템으로 변형해 출력하는 컴포넌트를 만들었다.
하나씩 살펴 보자면
struct RainbowTextForEach<Content>: View where Content: View이 컴포넌트는 View 타입의 파라미터를 제네릭으로 받는다. 여기서 Content는 타입 파라미터로 받는다.
struct RainbowTextForEach<Content:View>: View이렇게도 수정할 수 있다.
그리고
let content: () -> Content타입 파라미터를 반환하는 클로져를 생성해주고
init(@ViewBuilder content: @escaping () -> Content) { self.content = content }초기화를 해주는데, 여기서 content를 @escaping 클로져로 초기화하는 이유는 뷰의 생명주기와 관련이 있다. 만약 escaping이 아니라면 이 컴포넌트가 동작하는 중에는 다른 코드는 동작하지 않을 것이다.
그래서 이 부분처럼
ForEach(rainbow,id: \.self){ color in content() .foregroundColor(color) }content를 ForEach스코프에서 기존에 준비 되었던 색깔 배열 수 만큼 돌려 무지개 뷰를 만들 수 있다.
RainbowTextForEach{ VStack{ Text("안녕하세요") RoundedRectangle(cornerRadius: 10) } }
@ViewBuilder의 역할은 여기서 끝난게 아니다.
개발하다가 이런 에러를 본적이 있을 것이다.
(아님 말구..)
기존에 이걸 해결하려면 Z/H/VStack이나 뭐 Group,ForEach등등 스코프에 삽입했어야 했을 것이다. 하지만 우리는 이제 알 수 있다. 방금 언급한 것들 모두 @ViewBuilder속성으로 정의 되어있다.
그럼 얘네도 그렇게 정의하면..?

에러가 사라진다..!
"이렇게 굳이 할필요가 있나?" 라고 생각한다면 이것을 보아라


왼쪽은 hello메서드를 VStack안에 삽입, 오른쪽은 HStack에 삽입했을 때 나오는 결과다.
뷰 메서드를 @ViewBuilder로 선언할 경우 이렇게 유동적으로 뷰를 정의할 수 있게 된다.
'iOS > SWIFTUI' 카테고리의 다른 글
[SwiftUI] - GeometryReader (0) 2024.07.26 [SwiftUI] #문법 - @ObservedObject (0) 2023.04.14 [SWIFTUI] #문법 - @Binding (1) 2023.01.04 [SWIFTUI] #문법 - @State (0) 2022.10.09