1. はじめに
SwiftUI = コード見ただけで画面のイメージ湧くくらいスッキリしてる。
VStack {
Text("Hello")
Image(systemName: "star")
Button("Tap me") {
print("Tapped!")
}
}
↑こんな感じで、UIがまるでHTMLやスクリプトみたいに宣言的に書ける。
UIKitの頃の「View作って、addSubviewして、AutoLayout書いて…」ってのが不要になってかなり楽になった。
でも、これがどうして可能なのかというそもそもの疑問:
Swiftの通常の関数 = 基本1つの値しか返せない
でも上のコード、TextもImageもButtonも返してる。
ここで登場するのが――@ViewBuilder
という機能
2. @ViewBuilderとは?
一言で言うと
複数のViewをまとめて“返せるようにする”ための属性(アノテーション)
正式には @ViewBuilder
は SwiftUI が使ってる
「Result Builder(結果ビルダー)」という機能の一種。
SwiftUIでのUIコードを、あの宣言的なスタイルに見せかけてくれてるのがこれ。
なんで必要?
Swiftの関数やクロージャは、普通は1つの値しかreturnできない。
でも、SwiftUIは VStack {}
の中に複数のViewを返してる
その “複数のView” をいい感じに組み立てて1つのViewとして返してくれてるのが、@ViewBuilder
の仕事。
VStack {
Text("Hello")
Text("World")
}
↑これは裏で @ViewBuilder
が「Text(“Hello”)とText(“World”)をくっつけて、1つのViewにまとめといたぞ」ってやってくれてる。
どこで使われてるのか?
VStack
,HStack
,ZStack
,NavigationStack
ForEach
,if
やswitch
を使った条件Viewの切り替え- 自分でカスタムViewの中にView返す関数作るとき
3. @ViewBuilderの特徴
Swiftの関数は基本「1つだけ」しか返せない
Swiftでは関数やクロージャの戻り値は、原則1つだけ。
func makeView() -> some View {
Text("Hello")
Text("World") // ❌ エラー
}
↑みたいに、複数のViewを直列で返すことはできない。
SwiftUIのViewは「ツリー構造」
SwiftUIでは画面全体がViewの木(View Tree)になってる。
つまり、「親Viewが子Viewを持って」「その子がまた子を持って」っていうツリー構造。
VStack {
Text("A")
HStack {
Text("B")
Text("C")
}
}
↑こんな感じで、親の VStack
が子として Text
, HStack
を持って、HStack
がさらに子を持って…って構造になる。
でもこのツリーをコードで自然に書けるのは、裏で @ViewBuilder
が「いい感じに木を組み立ててくれてる」から。
if / ForEach も書ける理由
VStack {
if isLoggedIn {
Text("Welcome back!")
} else {
Text("Please sign in")
}
ForEach(items) { item in
Text(item.title)
}
}
↑こんな「ロジック入りのUIコード」が書けるのも、@ViewBuilder
が ifやForEachの中のViewをうまく合成してくれるから。
なかったら、いちいちViewをArrayにしたり、Groupでくくったりして大変。
4. 実際の使用例
✅ 1. VStackで複数のViewを返す例
VStack {
Text("Hello")
Text("World")
}
このクロージャ、実は中身が @ViewBuilder
になってるから
Textを2つ並べてもOK。
✅ 2. 条件分岐 (if
) の例
VStack {
if showGreeting {
Text("Hey there!")
}
Text("Always here")
}
通常の関数なら、if
の中でreturn分けるとかは不可能。@ViewBuilder
なら **「条件がfalseなら何も表示しない」**ってのも自然に扱える。
✅ 3. ループ (ForEach
) の例
VStack {
ForEach(0..<3) { i in
Text("Item \(i)")
}
}
これも、ForEach
が @ViewBuilder
によって、複数のTextをViewツリーに展開してくれてる感じ。
✅ 4. カスタムView(関数)で使う例
@ViewBuilder
func greetingView(isHappy: Bool) -> some View {
if isHappy {
Text("😁 Happy!")
} else {
Text("😐 Meh...")
}
}
@ViewBuilder
を関数につけると、中で複数のViewをreturnできるようになる。
これでロジック付きの再利用可能なView関数が作れて、UIがもっと柔軟になる、便利。
5. @ViewBuilderがないとどうなる?
コンパイルエラーになる例
Swiftは普通、関数の戻り値は1つだけ。
だから @ViewBuilder
なしで複数のViewを返そうとすると…容赦なくエラー。
func brokenView() -> some View {
Text("Hello")
Text("World") // ❌ エラー:「Unexpected non-void return value in void function」
}
書きづらくなる例
もし @ViewBuilder
がなかったら、複数Viewを返すには Group
や AnyView
を
無理やり使って手動でViewを包む必要がある。
func messyView() -> some View {
Group {
Text("Hello")
Text("World")
}
}
Before / After 比較
Before(@ViewBuilderなし)
func customView(isActive: Bool) -> some View {
Group {
if isActive {
Text("Active")
} else {
Text("Inactive")
}
}
}
After(@ViewBuilderあり)
@ViewBuilder
func customView(isActive: Bool) -> some View {
if isActive {
Text("Active")
} else {
Text("Inactive")
}
}
6. 注意点
1. Viewの数制限(Swift 5.9以前)
@ViewBuilder
で返せるViewの数は、最大10個まで(※ Swift 5.9未満)
それ以上になるとコンパイルエラー出るから注意。
VStack {
Text("1")
Text("2")
...
Text("11") // ❌ ここで怒られる、→ Swift 5.9以降は緩和されてるけど、10個超えたらViewをまとめる工夫はする
}
🧠 2. 複雑すぎるロジックは避ける
@ViewBuilder
の中でガチガチのロジック書くのは避ける。
@ViewBuilder
var body: some View {
if user.isActive && !user.isBlocked && settings.isFeatureEnabled {
// ❌ 複雑すぎて読めない
Text("Welcome!")
}
}
// → ビジネスロジックはなるべく外に出して、Viewの中は**“UIだけ”にするのが吉。
🚨 3. カスタムViewに乱用しない
なんでもかんでも @ViewBuilder
つけるのは危険かも。
struct CustomView: View {
let content: () -> some View
var body: some View {
content()
}
}
↑この場合、content
に @ViewBuilder
をつけるかどうかは呼び出し側のコード次第、
無意味にBuilderつけると、予期せぬバグや型エラーの温床になることもある。
必要な場面だけに絞って使うのがGood
7. まとめ
@ViewBuilder
は、SwiftUIの宣言的で自然なUIコードを支える最重要パーツ!- 複数のView、条件分岐、ループ…全部を1つのViewに合成できるのが魅力。
- でも、使いすぎや複雑なロジックは注意