NRIネットコム社員が様々な視点で、日々の気づきやナレッジを発信するメディアです

注目のタグ

    【SwiftUI】TabViewを使ってOnboardingを作成する

    概要

    TabViewを使って以下の様なアプリ起動時に表示するOnboardingを作成してみたいと思います!

    環境

    この記事は以下のバージョン環境のもと作成されたものです。
    【Xcode】14.0.1
    【iOS】16.0.2
    【macOS】Monterey バージョン 12.6

    TabViewの基本的な使い方

    TabViewを使用することで簡単にタブと、そのタブに応じたViewを表示するViewを生成する事ができます。

    サンプルコード

    struct OnboardingView: View {
        var body: some View {
            TabView {
                FirstTabView()
                    .tabItem {
                        Text("1")
                    }
                SecondTabView()
                    .tabItem {
                        Text("2")
                    }
            }
        }
    }
    
    struct FirstTabView: View {
        var body: some View {
            Text("FirstView")
        }
    }
    
    struct SecondTabView: View {
        var body: some View {
            Text("SecondView")
        }
    }
    

    TabViewの中に表示制御行いたいViewを定義します。
    そのViewに対して.tabItemとmodifierを追加します。
    .tabItem内はTextやImageを定義する事でタブ部分の表示に反映されます。

    Tabの背景色やアイコンに色を付ける方法

    iOS16から新しいmodifierが追加されたので再現方法がいくつかあります。
    まずUITabBar.appearance().backgroundColorを使用する方法です。

    struct OnboardingView: View {
        init(){
            UITabBar.appearance().backgroundColor = UIColor.yellow
        }
        var body: some View {
    ・・・
            }
    }
    

    選択しているタブアイコンの色はaccentColor modifierを使用します。

    TabView {
    ・・・
    }
    .accentColor(.red)
    

    ちなみに以下でもタブアイコンの色を変更できそうですができないです。

    .tabItem {
        Text("1")
            .foregroundColor(.red)
    }
    

    選択外のタブアイコンの色はUITabBar.appearance().unselectedItemTintColorを使用します。

    struct OnboardingView: View {
        init(){
            UITabBar.appearance().backgroundColor = UIColor.yellow
            UITabBar.appearance().unselectedItemTintColor = UIColor.red
        }
        var body: some View {
    ・・・
        }
    }
    

    また背景色に関してはiOS16からtoolbarBackgroundを使用することができます。

    TabView {
        FirstTabView()
            .tabItem {
                Text("1")
            }
            .toolbar(.visible, for: .tabBar)
            .toolbarBackground(
                Color.pink
                , for: .tabBar
            )
        SecondTabView()
            .tabItem {
                Text("2")
            }
    }
    

    Onboardingを作成

    tabViewStyleを使用することでタブのアイコンを非表示にして、スワイプ動作で各タブのViewを切り替える事ができます。

    struct OnboardingView: View {
        @State private var selectedPage = 1
        var body: some View {
            TabView(selection: $selectedPage) {
                FirstTabView()
                    .tabItem {
                        Text("1")
                            .foregroundColor(.red)
                    }
                SecondTabView()
                    .tabItem {
                        Text("2")
                    }
            }
            .tabViewStyle(.page(indexDisplayMode: .never))
        }
    }
    

    今回はIdentifiableに準拠したstructを作って、ForEachで各タブのViewを生成したいと思います。
    ForEachの使い方がわからない方は是非以下の記事見てください。

    tech.nri-net.com

    struct Onboarding: Identifiable {
        var id: Int { pageNum }
        var pageNum: Int
        var title: String
        var description: String
    }
    

    作成したstructを使用してViewを作ります。

    struct OnboardingView: View {
        @State private var selectedPage = 1
        let onbordings: [Onboarding] = [
            .init(pageNum: 1, title: "ようこそhogeアプリへ!", description: "このアプリはhogehogeです!"),
            .init(pageNum: 2, title: "使い方の説明だよ!", description: "使い方はhogehogeです!"),
            .init(pageNum: 3, title: "その他の説明だよ!", description: "その他はhogehogeです!"),
            .init(pageNum: 4, title: "最後に!", description: "この記事がよかったらいいねしてね!")
        ]
        var body: some View {
            VStack {
                TabView(selection: $selectedPage) {
                    ForEach(onbordings) { index in
                        VStack(alignment: .leading) {
                            VStack(alignment: .leading) {
                                HStack {
                                    RoundedRectangle(cornerRadius: 5)
                                        .foregroundColor(.black.opacity(0.7))
                                        .frame(width: 10, height: 50)
                                    Text("Step.\(index.pageNum)")
                                        .font(.largeTitle)
                                }
                                VStack(alignment: .leading) {
                                    Text(index.title)
                                        .font(.title2)
                                        .foregroundColor(.purple)
                                    Text(index.description)
                                }
                            }
                            .padding(.leading, 5)
                            Image("\(index.pageNum)")
                                .resizable()
                                .aspectRatio(contentMode: .fit)
                        }
                        .tag(index.pageNum)
                    }
                }
                .tabViewStyle(.page(indexDisplayMode: .never))
                Spacer()
                ZStack {
                    Capsule()
                        .frame(width: 100, height: 20)
                        .foregroundColor(.gray.opacity(0.2))
                    HStack {
                        ForEach(onbordings) { index in
                            Circle()
                                .foregroundColor(.purple.opacity(selectedPage == index.pageNum ? 1 : 0.2))
                                .frame(width: 10, height: 10)
                                .onTapGesture {
                                    selectedPage = index.pageNum
                                }
                        }
                    }
                }
            }
            .animation(.easeInOut, value: selectedPage)
        }
    }
    

    tabViewStyleでタブの部分が可視化されていないので擬似的にCircleで作成し、onTapGestureでselectedPageにindex.pageNumを渡す事でページを飛ばして遷移できるようになっています。
    またanimationを付ける事でスワイプ動作と同じ様にスライドして遷移しているように表現できます。

    まとめ

    TabViewを使用してOnboardingを作ってみました!
    時間ができたら汎用的に使用できる様に作り直してPackage化して公開したいと思います!

    執筆者岡優志

    iOSエンジニア
    iOSを専門とし、モバイルアプリの開発を行なっています。

    Twitter