モバイルアプリは画面が小さいため「画面遷移」があると便利です。特にタッチパネルに対応したiOSデバイスならスワイプ動作による画面遷移を使いたくなります。ここではSwiftUIのTabViewを使ってスワイプによる画面遷移機能を実装する方法や、各画面間におけるデータのやり取りについて説明します。
こんにちは。wat(@watlablog)です。ここではSwiftUIのTabViewを使ってスワイプによる画面遷移機能を実装する方法を紹介します!
目標と動作環境
目標:スワイプの実装とデータ通信の基礎を習得する
この記事ではiOSデバイスにおけるアプリ開発を前提として、Swiftによるコーディングを行います。iPhone等のモバイルアプリでお馴染みのスワイプ機能を実装することを目標とします。
また、画面遷移機能を実装した場合は画面間のデータ通信も気になるところです。そのためこのページでは画面遷移機能を紹介するとともに、簡単なデータ通信例を示すところまでを記載します。
スワイプ機能といえば、WATLABブログでは過去PythonとkivyによるGUIアプリ制作時に実装したことがあります。イメージは同じですので、気になる方はこちらの記事をご覧ください。
・kivyでピーク検出機能付き簡易FFTアナライザを作ってみた
動作環境
このページのコードは以下の環境で動作確認を行いました。
Mac | OS | macOS Ventura 13.2.1 |
---|---|---|
CPU | 1.4[GHz] | |
メモリ | 8[GB] | |
Xcode | Version | 14.3(14E222b) |
Swift | Version | 5.8(swiftlang-5.8.0.124.2 clang-1403.0.22.11.100) |
iPhone SE2 | OS | iOS 16.3.1 |
まずはタブを作ってみるサンプルコード
スワイプ機能にはTabViewを使いますが、TabViewはその名の通り「タブ」なので、スワイプ用ではありません。まずは基本機能として通常のタブの使い方を見てみましょう。
タブを使ったWATLABブログの過去記事はこちら。事例を知りたい方は是非こちらもご覧ください。
・アプリ完成編:wxPythonで信号処理のGUIアプリをつくろう⑥
シンプルなタブ
TabViewの基本的な使い方のみを使用した画面遷移コードをこちらに示します。
TabView(){}で全体を囲み、VStack等のブロックを複数用意するだけでタブ化できます。.tabItem{}メソッドを使うことでタブの表示名を設定可能です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
import SwiftUI struct ContentView: View { var body: some View { // UI TabView() { VStack { Text("Hello, world!") } .padding() .tabItem{ Text("Page1") } VStack { Text("Hello, Swift!") } .padding() .tabItem{ Text("Page2") } } .font(.largeTitle) .edgesIgnoringSafeArea(.top) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } |
このコードを実行した結果がこちらの動画です。タブをクリックすることで画面遷移ができています。
システムイメージ(SF Symbols)を使ったタブ
ちょっとおしゃれに、タブ表示名に画像をつけてみましょう。XcodeであればSF Symbolsという現時点で4400種以上の記号群を使うことができます。公式ページからライブラリをダウンロード可能ですが、しなくても使えます。ダウンロードすることで使いたいシンボルを検索できるので便利です。
↓公式ページはこちら
・SF Symbols:https://developer.apple.com/sf-symbols/
使い方は簡単で、.tabItemにImageを設定して4400種類以上から選んだファイル名を記載するだけです。
1 2 3 4 |
.tabItem{ Image(systemName: "mic.circle.fill") Text("Record") } |
結果はこちら。iOSアプリっぽさが増しました!
ちなみに、.accentColor(.black)を.font等を設定しているところに書くことでアクセントカラーを変更可能です(blackの部分を任意の色に変更してください)。
スワイプ機能の実装を目指して記事を書き始めましたが、こちらのタブ表示もかなり良いかも知れません!
タブの各ページ間でデータ通信をする
タブの各ページ間でデータ通信するのは非常に簡単です。「Swift入門:最低限覚えておく基礎文法の備忘録」で紹介したように@Stateで変数を宣言する方法がそのまま使えます。
こちらのコードはInputページにテキスト入力フォームを用意し、ユーザーが入力した値(文字)をOutputページに表示させるだけのものです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
import SwiftUI struct ContentView: View { @State private var inputText = "Initial Text" var body: some View { // UI TabView() { VStack { Text(inputText) } .padding() .tabItem{ Image(systemName: "display") Text("Output") } VStack { TextField("Enter text", text: $inputText) .textFieldStyle(RoundedBorderTextFieldStyle()) .padding() } .padding() .tabItem{ Image(systemName: "pencil") Text("Input") } } .accentColor(.black) .font(.largeTitle) .edgesIgnoringSafeArea(.top) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } |
結果はこちら。問題なくデータ通信できています。
classとTabViewの各ページ間でデータ通信をする
以下の記事で紹介したプログラムはclassを使っていました。複雑なアプリの場合はclassとのデータ通信方法を確認しておくことも重要です。
・SwiftUIとAVFoundationで音声を再生する方法
以下にシンプルなclassを定義してTabViewとのデータ通信を行っている例を示します。
このコードはclass DataModelを定義して、ContentView内で@StateObjectとしてインスタンス化しています。classに色々なプロパティを定義しておけば、インスタンス化されたオブジェクト内で自由にデータを保持し、ContentView内で使えるというわけです。これはSwiftに限らず共通の事項と思いますので是非覚えておきましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
import SwiftUI class DataModel: ObservableObject { @Published var text: String = "Initial Text" } struct ContentView: View { @StateObject private var dataModel = DataModel() var body: some View { TabView { VStack { Text(dataModel.text) } .padding() .tabItem { Image(systemName: "display") Text("Output") } VStack { TextField("Enter text", text: $dataModel.text) .textFieldStyle(RoundedBorderTextFieldStyle()) .padding() } .padding() .tabItem { Image(systemName: "pencil") Text("Input") } } .accentColor(.black) .font(.largeTitle) .edgesIgnoringSafeArea(.top) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } |
上記コードの実行結果は先ほどの動画と同じなので割愛します。
classを覚えるとプログラミングの幅が広がります。まだ概念があやしいという方は以下の記事が参考になると思います(Pythonで説明していますが考え方の部分は同じです)。
・Pythonのクラスの使い方とオブジェクト指向の考え方を理解する
TabViewでスワイプによる画面遷移機能を実装するSwiftコード
スワイプ機能の実装
それではこのページの最終目標であるスワイプ機能の実装について説明しましょう。といっても上記TabViewの使い方を習得した後はあっけないくらい簡単です。
以下のコードが画面遷移をスワイプによって行う方法です。.tabItemはあってもなくても良いのですが、.tabViewStyle(PageTabViewStyle())をつけるだけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
import SwiftUI struct ContentView: View { @State private var inputText = "Initial Text" var body: some View { // UI TabView() { VStack { Text(inputText) } .padding() VStack { TextField("Enter text", text: $inputText) .textFieldStyle(RoundedBorderTextFieldStyle()) .padding() } .padding() } .font(.largeTitle) .edgesIgnoringSafeArea(.top) // スワイプ動作をさせるために設定 .tabViewStyle(PageTabViewStyle()) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } |
こちらが結果の動画です。簡単にスワイプ動作で画面遷移ができました!
TabViewでプログラム的に画面遷移する方法
以下のコードは「@State private var selection」を宣言し、TabViewの中に「selection: $selection」を記入し、各タブに明示的に「.tag(0)」と記載することでtagの番号で画面遷移できるようにしたプログラムです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
import SwiftUI struct ContentView: View { // 画面をプログラム的に遷移させるために設定 @State private var selection = 0 var body: some View { // UI TabView(selection: $selection) { VStack { Button("Jump to next!"){ selection = 1 } } .padding() .tag(0) VStack { Button("Back to front!"){ selection = 0 } } .padding() .tag(1) } .font(.largeTitle) .edgesIgnoringSafeArea(.top) .tabViewStyle(PageTabViewStyle()) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } |
こちらが実行結果です。ボタンアクションで飛ぶ動作も実装できました。
まとめ
今回は以上です。SwiftUIによる画面遷移として、まず初めにTabViewの基本的な動作を学びました。そして次にスワイプ動作の実装方法について学びました。
TabViewは.tabItem{}を使うことで一層iOSアプリ感を醸し出すのでテンションが上がります。.tabItem{}とトレードオフの関係になってしまいますが、.tabViewStyle(PageTabViewStyle())によるスワイプ動作の実装もできました。データのやり取りは面倒かと思いきや、今までと何ら変わることのない内容でいけそうです。
個人的にはSF Symbolsを使って感覚的に使えるアプリの方が良いかもと思い始めていますが、Case by caseで使い分けましょう。
.tabItemによる画面遷移とスワイプによる画面遷移はネストして組み合わせもできるかな?
着実にアプリ制作に必要なスキルが身についていきます!
Twitterでも関連情報をつぶやいているので、wat(@watlablog)のフォローお待ちしています!