システム開発<基本編>SwiftUIでベーシックなTabViewと画像を回転させるアプリケーション

Swift

前回は、SwiftUIで、回転木馬(メリーゴーランド)のように画像自動スライド・循環する

カルーセルアプリの開発記事を紹介しました。

 

 

今回は、SwiftUIで、ベーシックTabView画像回転させるアプリケーションを開発する

記事を紹介しようと思います。

SwiftUIでベーシックなTabViewと画像を回転させるアプリケーション

 

 

習得内容

今回、SwiftUIで、ベーシックTabView画像回転させるアプリケーションを作成

する事で、下記の内容を習得する事が出来ます。

  • TabView子Veiw 6つ以上になると、More というリストTab になり、5つ目以降Tab が格納されます。
  • NavigationStack3種類画面遷移から、配列コントロールした遷移を実現します。
  • SwiftUI View名称の登録エラーVew名称正しく登録して、無駄なエラー減らしましょう。
  • RotateGesture画像任意回転させる事が出来ます。
  • TapGesture(count: 3)3回押した(トリプルタップした)時のアクションを実行します。
  • Liquid GlassiOS26.0以降から採用された、ガラスっぽい見た目?Tab になります。

 

 

動作環境

今回も、アプリが動作する環境は下記になります。

  • mac OS:Sequoia 15.6.1
  • iOS:26.0
  • Xcode:26.0.1
  • Swift:6.2.1

 

 

NavigationStack

NavigationStack については、過去の記事で詳しく説明しましたので、今回も、

詳細説明省略させて頂きます。

 

NavigationStack → TabView → SelectTabView

NavigationStack で、配列コントロールを実現する為に、View を作成して行きます。

  1. TabViewベーシックTabView で、画像回転させたり、3回押した(トリプルタップした)時のアクションを実現します。
  2. SelectTabView子Veiw 6つ以上になると、More というリストTab になり、5つ目以降Tab が格納されます。

 

Xcode の上部タブメニューより、File → New → File from Template を選択します。

 

SwiftUI View を選択します。

 

名称は、TabVeiw を入力してみます。

 

Create ボタンを押して、TabVeiw.swift を作成します。

 

次に、SelectTabVeiw を作成する為、再度、Xcode の上部タブメニューより、

File → New → File from Template を選択します。

 

SwiftUI View を選択します。

 

名称は、SelectTabVeiw を入力してみます。

 

Create ボタンを押して、SelectTabVeiw.swift を作成します。

 

SwiftUI View名称の登録エラー

TabVeiw.swiftSelectTabVeiw を作成して処理の実装を進めて行きますが、今回、

致命的な間違いをしていました。

処理の実装を進めて行き、問題無いかどうか確認の為、アプリの実行を試してみました。

Xcode の上部タブメニューより、Product → Run を選択したところ、下記のような

コンパイルエラーとなりました。

 

 

う、う、動かない…

TabVeiw.swift を登録した途端動かなくなり、色々と調査を行い、Xcode変更

コンポーネント変更が有ったのか?とか、良からぬ事を考えながら、丸半日の時間

原因調査費やして(無駄にして?)しまっていました。

 

実は、上記に記載したように、TabVeiw.swift を登録した】ことが原因でした。

TabVeiw 自体が標準コンポーネントなので、SwiftUI View の登録で、同名称で登録すると

動かなくなるのは当然で、アホですよね。

 

同じような事は過去にも有り、Pythonwordcloud の実装でも、やらかしてしまいました。

ライブラリwordcloud インポートしているにも関わらず、wordcloud同名称

登録して動かなくなりました。

wordcloud の記事には記載しませんでしたが、無駄な時間を費やさないように、

気を付けましょう。

 

TabVeiw から BaseTabView へ修正

登録した TabVeiw.swift削除して、新たに別名で作成し直しても良いのですが、

せっかくなので、ソースを直接、修正します。

 

 

上記のファイル名と、Struct右側#Preview3箇所を、TabVeiw から BaseTabVeiw

に修正します。

 

 

BaseTabVeiw.swift に修正しましたので、処理の実装を進めます。

NavigationListVeiw のソースも、TabVeiw から BaseTabVeiw に修正する必要が

有りますので、下部の NavigationListVeiw のソースで説明します。

 

ContentView:メインメニュー(ランチャー)

メインメニュー(ランチャー)ContentView を確認します。

 


import SwiftUI

struct ContentView: View {

    @State var isPopupView_1 = false
    @State var isPopupView_2 = false
    @State var isPopupView_3 = false
    @State var isPopupView_4 = false
    @State var isPopupView_5 = false

    var body: some View {
        
        VStack{
            Button {
                self.isPopupView_1.toggle()
            } label: {
                Text("QRCodeView")
                    .font(.title2)
            }
            .fullScreenCover(isPresented: $isPopupView_1, content: {
                QRCodeView(isPopupView_1: $isPopupView_1)
            })
            .padding()

            Button {
                self.isPopupView_2.toggle()
            } label: {
                Text("ColorPickerView")
                    .font(.title2)
            }
            .sheet(isPresented: $isPopupView_2, content: {
                ColorPickerView(isPopupView_2: $isPopupView_2)
            })
            .padding()

            Button {
                self.isPopupView_3.toggle()
            } label: {
                Text("ColorSliderRGBView")
                    .font(.title2)
            }
            .sheet(isPresented: $isPopupView_3, content: {
                ColorSliderRGBView(isPopupView_3: $isPopupView_3)
            })
            .padding()

            Button {
                self.isPopupView_4.toggle()
                
            } label: {
                Text("ColorSliderHSBView")
                    .font(.title2)
            }
            .sheet(isPresented: $isPopupView_4, content: {
                ColorSliderHSBView(isPopupView_4: $isPopupView_4)
            })
            .padding()

            Button {
                self.isPopupView_5.toggle()
                
            } label: {
                Text("NavigationListView")
                    .font(.title2)
            }
            .sheet(isPresented: $isPopupView_5, content: {
                NavigationListView(isPopupView_5: $isPopupView_5)
            })
            .padding()
        }
    }
}

#Preview {
    ContentView()
}

 

メインメニュー(ランチャー)は、以下の内容です。

  • QRCodeView
  • ColorPickerView
  • ColorSliderRGBView
  • ColorSliderHSBView
  • NavigationListView

今回も、メインメニュー(ランチャー)ContentView に変更は有りませんので、

このまま利用して行きます。

 

NavigationListView:配列コントロール

NavigationListVeiw のソースに、追記・修正をして行きます。

 

ソース例:NavigationListVeiw(配列コントロール)

前回の記事で、CarouselView(path: $path) を追加した NavigationListVeiw のソースを

確認してみます。

 


import SwiftUI

struct NavigationListView: View {

    @Binding var isPopupView_5: Bool
    @State var path: [String]=[]

    var navi_lists = [
        "SwipeView",
        "CarouselView",
        "TabView",
        "TabBarView"
    ]

    var body: some View {
        VStack {

            NavigationStack(path: $path) {

                List(navi_lists, id: \.self) { navi_list in

                    NavigationLink(navi_list, value: navi_list)
                        .font(.system(size: 22))
                }
                .navigationDestination(for: String.self) { navi_list in

                    switch (navi_list) {

                    case "SwipeView":
                        SwipeView(path: $path)
                            .navigationTitle(navi_list)
                            .navigationBarTitleDisplayMode(.inline)

                    case "SwipeDetailView":
                        SwipeDetailView(path: $path)
                            .navigationTitle(navi_list)
                            .navigationBarTitleDisplayMode(.inline)

                    case "CarouselView":
                        CarouselView(path: $path)
                            .navigationTitle(navi_list)
                            .navigationBarTitleDisplayMode(.inline)

                    case "TabView":
                        Text(navi_list)
                            .font(.system(size: 22))
                            .foregroundColor(.green)
                            .padding()

                    case "TabBarView":
                        Text(navi_list)
                            .font(.system(size: 22))
                            .foregroundColor(.orange)
                            .padding()

                    default:
                        Text(navi_list)
                            .font(.system(size: 22))
                            .foregroundColor(.blue)
                            .padding()
                    }
                }
                .navigationTitle("NavigationList")
                .font(.system(size: 22))
                .foregroundColor(.purple)
            }
        }

        Button(action: {
            withAnimation {
                self.isPopupView_5.toggle()
            }
        }, label: {
            Text("Close")
                .font(.title2)
                .foregroundColor(.black)
        })
        .padding()
    }

}

#Preview {
    NavigationListView(isPopupView_5: .constant(false))
}

 

ソース例:NavigationListVeiw(配列コントロール:追加・修正版)

NavigationListVeiw のソースに、追記・修正をしたソースを紹介します。

 


import SwiftUI

struct NavigationListView: View {

    @Binding var isPopupView_5: Bool
    @State var path: [String] = []

    var navi_lists: [String] = [
        "SwipeView",
        "CarouselView",
        "BaseTabView",
        "TabBarView"
    ]

    var body: some View {
        VStack {

            NavigationStack(path: $path) {

                List(navi_lists, id: \.self) { navi_list in

                    NavigationLink(navi_list, value: navi_list)
                        .font(.system(size: 22))

                }
                .navigationDestination(for: String.self) { navi_list in

                    switch (navi_list) {

                    case "SwipeView":
                        SwipeView(path: $path)
                            .navigationTitle(navi_list)
                            .navigationBarTitleDisplayMode(.inline)

                    case "SwipeDetailView":
                        SwipeDetailView(path: $path)
                            .navigationTitle(navi_list)
                            .navigationBarTitleDisplayMode(.inline)

                    case "CarouselView":
                        CarouselView(path: $path)
                            .navigationTitle(navi_list)
                            .navigationBarTitleDisplayMode(.inline)

                    case "BaseTabView":
                        BaseTabView(path: $path)
                            .navigationTitle(navi_list)
                            .navigationBarTitleDisplayMode(.inline)

                    case "SelectTabView":
                        SelectTabView(path: $path)
                            .navigationTitle(navi_list)
                            .navigationBarTitleDisplayMode(.inline)

                    case "TabBarView":
                        Text(navi_list)
                            .font(.system(size: 22))
                            .foregroundColor(.orange)
                            .padding()

                    default:
                        Text(navi_list)
                            .font(.system(size: 22))
                            .foregroundColor(.blue)
                            .padding()
                    }
                }
                .navigationTitle("NavigationList")
                .font(.system(size: 22))
                .foregroundColor(.purple)
                
            }
        }

        Button(action: {
            withAnimation {
                self.isPopupView_5.toggle()
            }
        }, label: {
            Text("Close")
                .font(.title2)
                .foregroundColor(.black)
        })
        .padding()
    }
}

#Preview {
    NavigationListView(isPopupView_5: .constant(false))
}

追記・修正をした部分を説明します。

  • var navi_lists: [String] = […“BaseTabView”…]:TabVeiw から BaseTabVeiw に修正しています。
  • case “BaseTabView”TabVeiw から BaseTabVeiw に修正しています。
  • BaseTabView(path: $path)BaseTabView に、path の引数を渡しています。
  • SelectTabView(path: $path)SelectTabView に、path の引数を渡しています。

 

BaseTabView → SelectTabView

BaseTabVeiwSelectTabView のソースを紹介して行きますが、今回も事前に、

Viewで利用する画像(写真)Xcode に追加して行きます。

 

画像(写真)の追加

著作権肖像権考慮して、今回も、筆者の家で飼っている写真を利用します。

Finder から、好きな写真ドラッグ&ドロップします。

画像(写真)は、著作権肖像権問題が無いものを、各環境に合わせて、投入して下さい。

 

今回も、6枚写真ドラッグ&ドロップしてみました。

もし、画像(写真)名称をそのまま利用したく無い場合は、Assets 内で名称

変更する事も出来ますので、試してみて下さい。

これで、SwiftUI のソースから直接、画像(写真)を呼び出して利用する事が出来ます。

 

 

ソース例:BaseTabView

NavigationStack から選択して、画像回転させたり、3回押した(トリプルタップした)

時のアクションを実現する BaseTabVeiw のソースを紹介します。

 


import SwiftUI

struct BaseTabView: View {

    @Binding var path: [String]
    @State private var magnifyBy: CGFloat = 1.0
    @State private var angle = Angle(degrees: 0.0)
    @State private var minZoom: CGFloat = 0.3
    @State private var maxZoom: CGFloat = 1.0

    var body: some View {
        ZStack {
            Color.white
                .edgesIgnoringSafeArea(.all)

            VStack {

                TabView {

                    Image("IMG_0541")
                        .resizable()
                        .scaledToFit()
                        .rotationEffect(angle)
                        .gesture(
                            RotateGesture()
                                .onChanged { value in
                                    angle = value.rotation
                                }
                        )
                        .tabItem {
                            Text("Angle")
                        }

                    Image("IMG_0232")
                        .resizable()
                        .scaledToFit()
                        .scaleEffect(magnifyBy)
                        .gesture(
                            TapGesture(count: 3)
                                .onEnded {
                                    withAnimation {
                                        magnifyBy = magnifyBy < maxZoom ? maxZoom:minZoom
                                    }
                                }
                        )
                        .tabItem {
                            Text("Zoom")
                        }
                }
                .padding()

                Button(action: {
                    withAnimation {
                        path.append("SelectTabView")
                    }
                }, label: {
                    Text("SelectTabView")
                        .font(.system(size: 22))
                        .foregroundColor(.mint)
                })
            }
        }
    }
}

#Preview {
    BaseTabView(path: .constant(["BaseTabView"]))
}
  • @State private var magnifyByズーム変化量を宣言しています。
  • @State private var angle = Angle(degrees: 0.0)角度変化量を宣言しています。
  • @State private var minZoomズーム最小値を宣言しています。
  • @State private var maxZoomズーム最大値を宣言しています。
  • TabView {:基本的な TabView を設定します。

 

RotateGesture

画像回転させる為に、RotateGesture() を利用します。

  • Image(“IMG_0541”)写真画像を設定しています。
  • .rotationEffect(angle)rotationEffect に、回転した angleバインドすることで、自動的任意角度回転します。
  • .gesture(gestureアクションを記載します。
  • RotateGesture()RotateGesture を呼び出しています。
  • .onChanged { value ingesture が、変化した時のアクションを記載します。
  • angle = value.rotationangle に、回転させた変化量を代入しています。
  • .tabItem { Text(“Angle”)Tab 内に、Angle テキスト(文字)を設定しています。

 

TapGesture(count: 3)

連続したタップアクションを実現する為に、TapGesture(count: 3 を利用します。

  • Image(“IMG_0232”)写真画像を設定しています。
  • .scaleEffect(magnifyBy)scaleEffect に、ズームした magnifyBy をバインドすることで、自動的ズームを実現します。
  • .gesture(gestureアクションを記載します。
  • TapGesture(count: 3)TapGesture() を呼び出しています。count: 3で、3回(トリプル)Tap 動作を検知した時に、アクションを実行します。
  • .onEnded {gesture が、終了した時のアクションを記載します。
  • magnifyBy = magnifyBy < maxZoom ? maxZoom:minZoommagnifyBy の値が maxZoom より小さい場合は、magnifyBymaxZoom を代入しています。magnifyBy の値が maxZoom より小さく無い場合は、magnifyByminZoom を代入しています。
  • .tabItem { Text(“Zoom”)Tab 内に、Zoom テキスト(文字)を設定しています。

 

今回は、シンプル(ベーシック)TabVeiw を表現しますので、Tab装飾させる為の

.tabViewStyle() や  .indexViewStyle() は、実装していません。

 

ソース例:SelectTabView

BaseTabVeiw から遷移して、子Veiw 6つ以上になると、More というリストTab

になり、5つ目以降Tabが格納される SelectTabView のソースを紹介します。

 


import SwiftUI

struct SelectTabView: View {

    @Binding var path: [String]

    var imageNames: [String] = [
        "IMG_0981",
        "IMG_0059",
        "IMG_0232",
        "IMG_0688",
        "IMG_0541",
        "IMG_0058",
    ]

    var body: some View {
        ZStack {
            Color.white
                .edgesIgnoringSafeArea(.all)

            VStack {

                TabView {

                    ForEach(imageNames, id: \.self) { imageName in
                        withAnimation {

                            VStack {

                                Image(imageName)
                                    .resizable()
                                    .scaledToFit()

                            }
                        }
                        .tabItem {
                            Label(imageName, systemImage: "cat")
                        }
                    }
                }

                Button(action: {
                    withAnimation {
                        path.removeAll()
                    }
                }, label: {
                    Text("NavigationListView")
                        .font(.system(size: 22))
                        .foregroundColor(.red)
                })
            }
        }
    }
}

#Preview {
    SelectTabView(path: .constant(["SelectTabView"]))
}
  • var imageNames: [String] = [“IMG_0981”, “IMG_0059”,…]Assets に保存した複数の画像(写真)名称を利用する為に、配列で宣言しています。
  • ForEach(imageNames, id: \.self) { imageName inAssets に保存した画像(写真)を、ForEach()配列画像(写真)繰り返し生成して表示します。
  • TabView {:基本的な TabView を設定します。
  • Image(imageName)配列から画像(写真)名称を利用して、表示しています。
  • .tabItem { Label(imageName, systemImage: “cat”) }Tab 内に、画像(写真)名称と、SF Symbolsアイコンを設定しています。

 

 

TabVeiw:前回のおさらい

TabView とは、複数子View 切り替えることのできる View です。

SwiftUIVeiw は、アプリの画面に表示されるものを指すので、画面画像だけではなく、

ボタン文字(テキスト)なども切り替えて、表示することが出来ます。

表示方法は、TabView {} の中に、View を入れる形になります。

上記のソースのように、TabView { Text(”…”)  Image(”…”) } の要領です。

Tab ですが、最大5つまで表示させる事が出来ます。Tab 6つ以上になると、More という

リストTab になり、5つ目以降Tab が格納されます。

 

 

アプリ実行:シミュレータ起動

では、実際にアプリを実行してみます。

Xcode の上部タブメニューより、Product → Run を選択するか、command + R

押してみて下さい。

 

エラーが無ければ、iPhoneシミュレータが起動します。

 

 

ContntVeiw(メインメニュー)が表示されました。

 

NavigationListView:ナビゲーション画面

続けて、NavigationListViewボタン押してみます(タップします)

 

 

ContntVeiw(メインメニュー)の上に NavigationListVeiw(ナビゲーション機能)

重なって、表示されました。

 

NavigationList の内容は、下記の通り TabVeiw から BaseTabVeiw に変更されている

事が確認出来ます。

  • SwipeView
  • CarouselView
  • BaseTabVeiw
  • TabBarView

現在の 配列(path の状態は、下記になります。

  • path = []

 

BaseTabView:画像の回転&トリプルタップ&Liquid Glass

次に、NavigationList 3段目BaseTabView を押してみます(タップします)

 

 

画面上部には、BaseTabView のタイトルが表示されていますが、

NavigationListVeiw で、タイトルを表示するように設定していました。

 

画像の下には、iOS26.0以降から採用された、ガラスっぽい見た目?

大きなTab Liquid Glass(青枠)が確認出来ます。

左側は、Angle と記載された選択中の大きな Tab(背景:グレー色)で、

右側は、Zoom と記載された大きな Tab(背景:白色)です。

 

現在の 配列(path の状態は、下記になります。

  • path = [“BaseTabView]

 

では、画像回転させてみます。

SwipeViewピンチイン、ピンチアウト(縮小・拡大)を実現した時のように、

画像の上で、キーボードの optionキー押した状態で、トラックパッド

2本の指で触れて、時計回り回転させてみて下さい。

 

 

画像が、斜め回転した事が確認出来ます。

更に、画像時計回り回転させてみます。

 

 

画像が、斜め下向き回転した事が確認出来ます。

 

今度は、画像の下にある大きな Tab 右側(Zoom)押して(タップして)

画面を切り替えてみます。

 

 

左側は、Angle と記載された大きな Tab(背景:白色)で、

右側は、Zoom と記載された選択中の大きな Tab(背景:グレー色)

に切り替わった事が確認出来ます。

 

ここでは、画像3回連続叩いて(トリプルタップして)みます。

 

 

画像が、縮小(約30%)された事が確認出来ます。

ここで、画像3回連続叩いて(トリプルタップして)みると、

元の大きさ戻る事も確認出来ます。

 

 

ソースに、withAnimation {}効果を入れているので、画像スムース縮小・拡大されて

いる事も確認が出来ると思います。

BaseTabView では、画像任意回転トリプルタップによる縮小・拡大を確認しました。

 

SelectTabView:複数Tabの表示

次に、大きな Tab の下に配置されている SelectTabView ボタン押して(タップして)

SelectTabView を表示します。

 

 

BaseTabView と似た画面に見えますが、画像の下にある大きな Tab 複数見え、

Tab の中にはアイコンが確認出来ます。

画面上部には、SelectTabView のタイトルが表示されていますが、

NavigationListVeiw で、タイトルを表示するように設定していました。

 

 

左端Tab アイコン&画像名のみ、選択中青色(背景:グレー色)が確認出来ます。

右端Tab の中には、点々「…」&More(紫枠)の文字が確認出来ます。

こちらは、TabView の説明でも記載していますが、Tab 6つ以上になると、More という

リストTab になり、5つ目以降Tab が格納されている状態です。

 

現在の 配列(path の状態は、下記になります。

  • path = [“BaseTabView“, “SelectTabView”]

 

次に、左端から2番目Tab 押して(タップして)みます。

 

左端から2番目Tab アイコン&画像名のみ、選択中青色(背景:グレー色)

確認出来ます。

 

次に、左端から3番目Tab 押して(タップして)みます。

 

 

左端から3番目Tab アイコン&画像名のみ、選択中青色(背景:グレー色)

確認出来ます。

こちらの画像は、BaseTabView トリプルタップによる縮小・拡大を確認した

画像と同じです。

 

次に、左端から4番目Tab 押して(タップして)みます。

 

左端から4番目Tab アイコン&画像名のみ、選択中青色(背景:グレー色)

確認出来ます。

 

SelectTabView:More

では、今度は、右端点々「…」&More Tab 押して(タップして)みます。

 

 

スライドして、アイコン&画像名リスト画面が表示されました。

画面上部には、SelectTabView のタイトルの下に、Moreサブタイトル

表示されています。

また、右端 Tab のみ選択されていて、点々「…」&Moreの文字が

青色(背景:グレー色)となっている事が確認出来ます。

 

では、上段リストアイコン&IMG_0541 押して(タップして)みます。

 

スライドして、5枚目画像が表示されました。

こちらの画像は、BaseTabView 画像任意角度回転させた画像と同じです。

 

では、Moreリスト画面戻ります。

 

 

画面左上に、「<」マーク2段に並んでいる事が確認出来ますので、2段目

「<」マーク(緑枠)押す(タップする)と、Moreリスト戻ります。

間違って上段「<」マーク押す(タップする)と、BaseTabView 戻りますので、

ご注意下さい。

 

 

では、2段目リストアイコン&IMG_0058 押して(タップして)みます。

 

 

スライドして、6枚目画像が表示されました。

 

SelectTabView では、複数の Tab 表示とMoreリスト画面を確認しました。

 

まとめ

今回は、SwiftUIで、ベーシックTabView画像回転させるアプリケーション

を開発記事を紹介しました。

 

次回は、SwiftUIで、TabBarPickerを利用したアプリケーションを開発する記事を

紹介しようと思います。

 

コメント

タイトルとURLをコピーしました