【SwiftUI】TabViewにButtonが埋め込めれないので代替方法を考えた

2023.02.07

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

今回はTabViewButtonを埋め込みたいと思ったのですが、埋め込めれそうになかった為、代替方法を試してみることにしました。

環境

  • Xcode 14.2
  • iOS 16.1

はじめに

ButtonをViewの下部に埋め込む方法としては、toolBarを使用する方法がありますが、

struct ContentView: View {

    var body: some View {

        NavigationStack {

            TabView {
                Text("List")
                    .tabItem {
                        Label("List",
                              systemImage: "list.bullet.rectangle")
                    }
                Text("Favorite")
                    .tabItem {
                        Label("Favorite",
                              systemImage: "star")
                    }
            }
            .toolbar {
                ToolbarItem(placement: .bottomBar) {
                    Button {
                        print("Add")
                    } label: {
                        Image(systemName: "plus")
                    }
                }
            }
        }
    }
}

この方法だと、TabViewがある場合は、その下にToolbarItemが表示されて、とても気持ち悪い感じになってしまいました。

また、TabView内にButtonを設置してもtoolbarのようには機能してくれません。このButtonで行いたいアクションをTabView内のコンポーネントが選択された時に実行出来れば問題は解決出来そうです。

選択されているTabを監視する

どのTabが選択されているか監視出来る様にSelectionを追加します。

struct ContentView: View {

    enum Selection {
        case list
        case favorite
        case addItem
    }

    @State private var selection: Selection = .list

    var body: some View {

        TabView(selection: $selection) {
            Text("List")
                .tabItem {
                    Label("List",
                          systemImage: "list.bullet.rectangle")
                }
                .tag(Selection.list)

            Text("Favorite")
                .tabItem {
                    Label("Favorite",
                          systemImage: "star")
                }
                .tag(Selection.favorite)

            Text("AddButton")
                .tabItem {
                    Image(systemName: "plus")
                }
                .tag(Selection.addItem)
        }
    }
}

これで、どのTabが選択されているかはselectionの値で監視が出来るようになりました。ただ、まだ監視が出来るだけで、AddButtonのTabを選択すると、AddButtonと表示されたViewに切り替わってしまいます。

今回は画面が切り替わることなくButton風に選択された時になんらかのアクションを実行したいのでもう少し手を加えていきます。

画面が切り替わることなく、アクションを実行する

AddButtonが選択された時に画面が切り替わることなく、なんらかのアクションを実行する為には、下記の2点が必要そうです。

  • AddButtonが選択されても画面が切り替わらない
  • AddButtonが選択された時にアクションを実行する

AddButtonが選択されても画面が切り替わらないようにする

前回のSelectionの値を保持するlastSelectionを追加し、onChange内で選択されたTabがaddItemなら前回のSelectionの値を代入するようにしました。

struct ContentView: View {

    enum Selection {
        case list
        case favorite
        case addItem
    }

    @State private var selection: Selection = .list
    @State private var lastSelection: Selection = .list

    var body: some View {

        TabView(selection: $selection) {
            Text("List")
                .tabItem {
                    Label("List",
                          systemImage: "list.bullet.rectangle")
                }
                .tag(Selection.list)

            Text("Favorite")
                .tabItem {
                    Label("Favorite",
                          systemImage: "star")
                }
                .tag(Selection.favorite)

            Text("AddButton")
                .tabItem {
                    Image(systemName: "plus")
                }
                .tag(Selection.addItem)
        }
        .onChange(of: selection) { _ in
            switch selection {

            case .list, .favorite:
                lastSelection = selection

            case .addItem:
                selection = lastSelection
            }
        }
    }
}

これでAddButtonを押しても、画面が切り替わらなくなりました。

AddButtonが選択された時にアクションを実行する

あとは、onChange.addItemだった時の分岐の中で行いたいアクションを実行するだけです。

.onChange(of: selection) { _ in
    switch selection {

    case .list, .favorite:
        lastSelection = selection

    case .addItem:
        print("Add Item or Do something")
        selection = lastSelection
    }
}

これでTabView内のItemが押された時にボタンのように画面が切り替わることなく何らかのアクションを実行出来るようになりました。

おわりに

結論としては、TabViewButtonは埋め込み出来ませんでしたが、Buttonのように何らかのアクションを実行することが出来ました。

今回はわかりやすくText("AddButton")としていますが、Text("")でも機能します。ただ、EmptyView()だとtabItem自体が表示されなくなるのでその他のViewを選択する必要があります。

画面下部にフローティングアクションボタンを配置するUIもありますが、もう少しUI的にスッキリさせれないかなと思い、今回この方法を試してみました。

同じような悩みを抱える方の助けになればと思います。