前回は、SwiftUIで、回転木馬(メリーゴーランド)のように画像を自動でスライド・循環する
カルーセルアプリの開発記事を紹介しました。
今回は、SwiftUIで、ベーシックなTabViewと画像を回転させるアプリケーションを開発する
記事を紹介しようと思います。
習得内容
今回、SwiftUIで、ベーシックなTabViewと画像を回転させるアプリケーションを作成
する事で、下記の内容を習得する事が出来ます。
- TabView:子Veiw が6つ以上になると、More というリストの Tab になり、5つ目以降のTab が格納されます。
- NavigationStack:3種類の画面遷移から、配列コントロールした遷移を実現します。
- SwiftUI View名称の登録エラー:Vew名称を正しく登録して、無駄なエラーを減らしましょう。
- RotateGesture:画像を任意に回転させる事が出来ます。
- TapGesture(count: 3):3回押した(トリプルタップした)時のアクションを実行します。
- Liquid Glass:iOS26.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 を作成して行きます。
- TabView:ベーシックな TabView で、画像を回転させたり、3回押した(トリプルタップした)時のアクションを実現します。
- 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.swift と SelectTabVeiw を作成して処理の実装を進めて行きますが、今回、
致命的な間違いをしていました。
処理の実装を進めて行き、問題無いかどうか確認の為、アプリの実行を試してみました。
Xcode の上部タブメニューより、Product → Run を選択したところ、下記のような
コンパイルエラーとなりました。
う、う、動かない…
TabVeiw.swift を登録した途端に動かなくなり、色々と調査を行い、Xcode の変更や
コンポーネントの変更が有ったのか?とか、良からぬ事を考えながら、丸半日の時間を
原因調査に費やして(無駄にして?)しまっていました。
実は、上記に記載したように、【TabVeiw.swift を登録した】ことが原因でした。
TabVeiw 自体が標準コンポーネントなので、SwiftUI View の登録で、同名称で登録すると
動かなくなるのは当然で、アホですよね。
同じような事は過去にも有り、Python で wordcloud の実装でも、やらかしてしまいました。
ライブラリの wordcloud をインポートしているにも関わらず、wordcloud と同名称で
登録して動かなくなりました。
wordcloud の記事には記載しませんでしたが、無駄な時間を費やさないように、
気を付けましょう。
TabVeiw から BaseTabView へ修正
登録した TabVeiw.swift を削除して、新たに別名で作成し直しても良いのですが、
せっかくなので、ソースを直接、修正します。
上記のファイル名と、Struct の右側、#Preview の3箇所を、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
BaseTabVeiw と SelectTabView のソースを紹介して行きますが、今回も事前に、
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 in:gesture が、変化した時のアクションを記載します。
- angle = value.rotation:angle に、回転させた変化量を代入しています。
- .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:minZoom:magnifyBy の値が maxZoom より小さい場合は、magnifyBy に maxZoom を代入しています。magnifyBy の値が maxZoom より小さく無い場合は、magnifyBy に minZoom を代入しています。
- .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 in:Assets に保存した画像(写真)を、ForEach()で配列の画像(写真)を繰り返し生成して表示します。
- TabView {:基本的な TabView を設定します。
- Image(imageName):配列から画像(写真)の名称を利用して、表示しています。
- .tabItem { Label(imageName, systemImage: “cat”) }:Tab 内に、画像(写真)の名称と、SF Symbols の猫のアイコンを設定しています。
TabVeiw:前回のおさらい
TabView とは、複数の 子View を切り替えることのできる View です。
SwiftUI の Veiw は、アプリの画面に表示されるものを指すので、画面や画像だけではなく、
ボタンや文字(テキスト)なども切り替えて、表示することが出来ます。
表示方法は、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で、TabBarとPickerを利用したアプリケーションを開発する記事を
紹介しようと思います。
- イラスト:いらすとや より引用




































コメント