SwiftUI로 View를 개발하다가 보면 조건에 따라 다른 View를 출력해야 하는 경우가 있습니다. 아래 예시를 보도록 하겠습니다.
아래 View는 원하는 도형의 이름을 입력 받아서 해당 도형을 보여주는 간단한 View입니다. 아래처럼 body 안에 if문으로 통해서 분기를 하게 되면 아무런 에러 없이 출력할 수 있습니다.
import SwiftUI
struct ViewBuilderView: View {
let shape: String
var body: some View {
if shape == "Circle" {
Circle()
} else if shape == "Rectangle" {
Rectangle()
} else {
Text("Invalid Shape")
}
}
}
똑같은 코드를 별도의 함수 안으로 옮겨보도록 하겠습니다. 큰 문제가 없는 코드처럼 보이지만 실상은 아래와 같은 에러를 마주치게 됩니다. 내용인 즉, 리턴 타입이 정의 되었는데 리턴문이 없다는 내용입니다. 그럼 리턴문을 추가하면 어떻게 될까요?
리턴문을 추가하면 선언된 some View의 타입과 일치하지 않는다는 에러로 바뀌게 됩니다. 원인은 함수가 리턴하는 타입이 조건에 따라 다르기 때문입니다. 즉 조건에 따라 리턴하는 View 타입이 Circle, Rectangle, Text로 서로 다르기 때문입니다.
전체를 그룹으로 묶고 리턴문을 제거하면 에러가 사라집니다. 리턴 타입이 Group 하나로 바뀌었기 때문입니다. 참고로 Group 안에는 위에 body 안에 구현하는 것과 동일하게 return문을 사용하지 않습니다.
func drawShape(shape: String) -> some View {
Group {
if shape == "Circle" {
Circle()
} else if shape == "Rectangle" {
Rectangle()
} else {
Text("Invalid Shape")
}
}
}
두 번째 방법은 서로 다른 리턴 타입 때문에 발생하는 문제를 AnyView를 통해 Type을 지워서 해결하는 방법입니다. AnyView를 통해서 타입을 지우면 리턴하는 View의 타입이 일치하므로 에러가 사라집니다.
func drawShape(shape: String) -> AnyView {
if shape == "Circle" {
return AnyView(Circle())
} else if shape == "Rectangle" {
return AnyView(Rectangle())
} else {
return AnyView(Text("Invalid Shape"))
}
}
세 번째이자 제가 가장 권장하는 방법은 ViewBuilder를 사용하는 방법입니다. 함수 ViewBuilder로 정의하면 body 안에 구현할 때와 동일한 방식으로 코드를 사용할 수 있습니다.
공식문서의 ViewBuilder의 정의도 multiple child view를 제공하기 위해서 활용한다고 정의된 것을 볼 수 있습니다.
@ViewBuilder
func drawShape(shape: String) -> some View {
if shape == "Circle" {
Circle()
} else if shape == "Rectangle" {
Rectangle()
} else {
Text("Invalid Shape")
}
}