🗻

【SwiftUI】enumと@ViewBuilderを使ってTabViewの可読性を向上させる方法

2024/01/04に公開

はじめに

TabViewをいい感じ(可読性と拡張性)を向上させる方法を扱います。

今回、扱うキーワードは、

  • TabView
  • enum
  • @ViewBuilder

です。

TabViewの例(Apple Developer documentationから引用):

image.png

https://developer.apple.com/documentation/swiftui/tabview

環境

  • OS:macOS Sonoma 14.2
  • Xcode:15.1 (15C65)
  • Swift:5.9.2

既存のコードをリファクタリングする

サンプルとして、TabViewを実装している次のようなContentViewをリファクタリングしていきます。

ContentView
struct ContentView: View {
    @State private var selectedTab = 0

    var body: some View {
        TabView(selection: $selectedTab) {
            Text("Home")
                .tabItem {
                    Image(systemName: "house")
                    Text("Home")
                }
                .tag(0)

            Text("Settings")
                .tabItem {
                    Image(systemName: "gearshape")
                    Text("Settings")
                }
                .tag(1)
        }
    }
}

enumを使って、各タブの持つ内容を一括で管理する

Tabというenumを作成します。
まず、今回は2つのTabを扱っているので、caseを次のように定義します。

Tab.swift
+ enum Tab: String {
+     case home = "Home"
+     case settings = "Settings"
+ }

@ViewBuilderを使用したコンピュ−テッドプロパティで、Viewの内容を定義する

さきほど、作ったenum@ViewBuilderを使用したコンピュ−テッドプロパティtabContentを追加します。tabContentは文字通り、TabのViewをクロージャで返すように実装しています。ここでは、switch文を使って、enumの各ケースに応じて異なるViewを生成しています。

Tab.swift
enum Tab: String {
    case home = "Home"
    case settings = "Settings"

+     @ViewBuilder
+     var tabContent: some View {
+         switch self {
+         case .home:
+             Image(systemName: "House")
+             Text(self.rawValue)
+         case .settings:
+             Image(systemName: "gearshape")
+             Text(self.rawValue)
+         }
+     }
+ }

ちなみに、@ViewBuilderは複数のビューを含むカスタムビューを作成する際に主に使用される属性です。プロジェクト作成時に作られるContentViewbodyプロパティにも暗黙的に使用されています。

bodyプロパティの定義
@ViewBuilder @MainActor
var body: Self.Body { get }

https://developer.apple.com/documentation/swiftui/view/body-8kl5o

最後に、ContentViewをリファクタリングする

さきほど、作成したTab.swiftの内容をContentViewに反映させていきます。主な変更は次の2点です。

  • tabItem(_:)メソッドでTab.(enumで定義したTab名).tabContentという形への書き換え

  • tag(_:)メソッドでTab.(enumで定義したTab名)という形へ書き換え

ContentView
struct ContentView: View {
-     @State private var selectedTab = 0
+     @State private var selectedTab: Tab = .home

    var body: some View {
        TabView(selection: $selectedTab) {
            Text("Home")
-                 .tabItem {
-                     Image(systemName: "house")
-                     Text("Home")
-                 }
-                 .tag(0)
+                 .tabItem { Tab.home.tabContent }
+                 .tag(Tab.home)

            Text("Settings")
-                 .tabItem {
-                     Image(systemName: "gearshape")
-                     Text("Settings")
-                 }
-                 .tag(1)
+                 .tabItem {  Tab.settings.tabContent }
+                 .tag(Tab.settings)
        }
    }
}

このようにenumを使うことで、Tabの定義を一元管理することができ、上記のように可読性を向上させることができました。また、Tabの数が増えた場合には、TabにCaseを追加することで簡単に拡張することができるようになりました。

おわりに

TabViewに直書きだったそれぞれのTabのコンテンツをenumを使って一箇所に記載することで可読性を向上させることができました。このリファクタリングによって、可読性の向上だけでなく、保守性、拡張性を向上させることができたと思います。

参考

https://developer.apple.com/documentation/swiftui/tabview

https://developer.apple.com/documentation/swiftui/view/body-8kl5o

https://heyjaywilson.medium.com/use-enums-for-tags-in-swiftui-1c3ce349d92c

https://youtu.be/vTmeKxEVY4A?si=eN0dLQrHm60DaTrt

Discussion