見出し画像

SwiftUIのTabViewでボタンアクションによるスライド切り替え

TabViewの基本的な使い方

SwiftUIでTabViewを表示するには、以下のようにTabViewの中にそれぞれのViewを配置し、.tabItemモディファイアでタブアイテムを設定できる。

struct ContentView: View {
    var body: some View {
        TabView {
            Page1()
                .tabItem {
                    Image(systemName: "1.circle.fill")
                    Text("Page1")
                }
            Page2()
                .tabItem {
                    Image(systemName:"2.circle.fill")
                    Text("Page2")
                }
        }
    }
}

struct Page1: View {
    var body: some View {
        Text("Page1")
    }
}
struct Page2: View {
    var body: some View {
        Text("Page2")
    }
}

TabViewStyle

  • DefaultTabViewStyle:iOSやmacOSで使用でき、スタイルを指定しない・automaticを指定する場合に表示されるスタイル。iOSでは画面下部に、macOSでは画面上部にタブアイテムが表示される。

  • PageTabViewStyle:iOSで使えるページをスワイプで切り替えられるスタイル

    • ページインジケータを表示したくない場合は.tabViewStyle(.page(indexDisplayMode: .never))を指定すればよい。

  • CarouselTabViewStyle:watchOSのみで使えるスタイル

ボタンアクションでスライド切り替え

DefaultTabViewStyleのように画面下部にタブアイテムを表示したくない、またはスワイプでのページングもしたくないなどの場合は、自前で設置したボタンからページ切り替え・スワイプアクションを無効にする必要がある。
今回は画面上部にカスタムのタブバーを配置し、ボタンアクションでスライドアニメーションでページを切り替え、さらにスワイプアクションも無効としている。

struct CustomSampleView: View {
    @State var selected = 0
        
    var body: some View {
        VStack(spacing: 0) {
            CustomTabBarView(selected: $selected)
            
            TabView(selection: $selected) {
                CustomPage1().tag(0)
                CustomPage2().tag(1)
                CustomPage3().tag(2)
            }
            // スワイプアクションを無効化
            .disabled(true)
            // ページスタイル(インジケータ非表示)
            .tabViewStyle(.page(indexDisplayMode: .never))
            // 切り替え時のアニメーション
            .animation(.easeInOut, value: selected)
        }
        .ignoresSafeArea(edges: .bottom)
    }
}

struct CustomTabBarView: View {
    @Binding var selected: Int
    
    var body: some View {
        HStack {
            CustomTabBarButtonView(selected: $selected, title: "Page1", tag: 0)
            CustomTabBarButtonView(selected: $selected, title: "Page2", tag: 1)
            CustomTabBarButtonView(selected: $selected, title: "Page3", tag: 2)
        }
    }
}

struct CustomTabBarButtonView: View {
    @Binding var selected: Int
    var title: String
    var tag: Int
    
    var body: some View {
        Button {
            selected = tag
        } label: {
            VStack(spacing: 0) {
                Text(title)
                    .padding(8)
                Rectangle()
                    .frame(height: 8)
                    .hidden(isHidden: selected != tag)
            }
        }
    }
}

struct CustomPage1: View {
    var body: some View {
        Color.red
            .ignoresSafeArea()
    }
}

struct CustomPage2: View {
    var body: some View {
        Color.green
            .ignoresSafeArea()
    }
}

struct CustomPage3: View {
    var body: some View {
        Color.blue
            .ignoresSafeArea()
    }
}

タブバーボタン下部のRectangleを選択状態に応じて非表示にしたいので、以下のような条件に応じてViewを非表示にするViewModifierを定義した。

/// 条件に応じてViewを非表示にするViewModifier
struct HiddenViewModifier: ViewModifier {
    var isHidden: Bool
    
    func body(content: Content) -> some View {
        if isHidden {
            content.hidden()
        } else {
            content
        }
    }
}

extension View {
    func hidden(isHidden: Bool) -> some View {
        modifier(HiddenViewModifier(isHidden: isHidden))
    }
}

サンプルコード

リポジトリ

参考


この記事が気に入ったらサポートをしてみませんか?