【初心者でも簡単】sheetで画面遷移を実装する方法【SwiftUI】

8 min

こんにちは、たいちです(@taichi_swift_)

たかし

たかし

画面が下から飛び出てくるやつってどうやって作るの?

結論: 【sheet】を使ってモーダル遷移(モーダルビュー)を作る

こんにちは、たいちです(@taichi_swift_)

今日の夜ご飯はUberEatsにしました。

食べたいもの全部をカートに入れて「カートを見る」ボタンを触った時に閃きました!

Modal遷移 sheetの例

UberEatsの「カートを見る」メニューをタッチした時に出てくる【モーダルビュー】

画面下部からビューが飛び出してきますよね。

モーダル遷移とも呼ばれます。(英語で書くとModalView)

アプリのレベルアップに繋がるsheetとは

アプリの画面を切り替える方法はいくつかあります。

iOSにおいて、画面の切り替えには様々な方法があります。

  • NavigationView, NavigationLinkを使用する方法(プッシュ遷移)
  • sheetを使う方法(モーダル遷移)
  • if や switch文、三項演算子などでViewを入れ替える方法

この中でも、sheetを使う方法は実装が簡単なので、今回はこちらを紹介していきます。

見慣れたプッシュ遷移とは違う画面遷移を実装したい時に便利です。

画面遷移の一種

UberEatsを例にすると、ホーム → 料理を選ぶ → サイズ、数量を選ぶまではNavigationViewでプッシュ遷移していますが、「カートを見る」ボタンはsheetでモーダル遷移しています。

純正のアプリでも使われている

UberEats以外にもiOS標準のリマインダーアプリの「リストを追加」ボタンだったり、カレンダーアプリのイベントを追加するボタンにもsheet(モーダル遷移)が使われています。

sheetをアプリで使うメリット

【メリット】
画面遷移のバリエーションを増やして、ユーザーがアプリで達成したい目的を阻害してしまうことを防ぐことができる。

sheetを使ったモーダル遷移の作り方

記事だけでなく動画化もしてみたので、好きな方を見てね!

たいち

たいち

変数(プロパティ)を定義する。

初心者の方は、ひとまずまっさらなViewを用意しましょう。

struct ContentView: View {
    var body: some View {
        Text("Hello World")
    }
}

ちなみに、これはViewの最小単位です。

そして、@State private var isShown: Bool = false というプロパティ(変数)を追加します。

追加する場所は、var body: some Viewと同じ階層です。(入れ子の深さが同じという意味です。)

struct ContentView: View {
    //ここに追加します!!!
    @State private var isShown: Bool = false
    var body: some View {
        Text("Hello World")
    }
}


このプロパティは、画面遷移のためのトリガーになります。

isShownというのは名前なので、自由に変更してみてくださいね!

isShown = falseがなんらかの方法でtrueになると、シートが飛び出てきます。

trueにする方法はなんでもいいのですが、オススメの方法をご紹介します!

画面遷移のトリガーをtrueにするボタンを定義する

ボタン[Button]か、.onTapGesture { }などでisShownをtrueにします。

struct ContentView: View {
    @State private var isShown: Bool = false
    var body: some View {
        //ボタンのactionでトリガーの変数をtrueにする方法
        Button(action: {
            self.isShown = true
        }) {
            Text("画面遷移する")
            //Image("画像の名前")や、VStack {}, Rectangle()などでも代用可。もちろん、.font(.title)などのmodifierも使用可能です。
        }
    }
}
//または、、、

struct ContentView: View {
    @State private var isShown: Bool = false
    var body: some View {
        //TextやImageをタップした時にトリガーする方法
        Text("画面遷移する")
            .onTapGesture {
                self.isShown = true
        }
        //他の処理もまとめて書くことができます。この方法だと勝手には文字が青くなりません。
    }
}

self.isShown = true が実行されたタイミングで画面遷移が発動します。

画面遷移のsheetを設定する方法

先ほど設定したトリガーの真下に、以下を記述します。

struct ContentView: View {
    @State private var isShown: Bool = false
    var body: some View {
        Button(action: { self.isShown = true }) {
            Text("画面遷移する")
        }
        .sheet(isPresented: self.$isShown) {
            //モーダル遷移した後に表示するビュー
            HStack {
                Text("モーダルビュー")
                Image(systemName: "paperplane")
            }
        }
    }
}

Image(systemName: “”)に関する解説はこちら 準備中です。

isPresentedの設定

.sheet(isPresented: self.$isShown) { //遷移後のビュー } 

isPresentedには、先ほど定義したトリガーのプロパティ(変数、値)をセットします。
変数の頭には、$マークをつけます。

selfはとりあえずつけてください。(自身のstructのプロパティを呼び出す時はselfをつけます。)

完成です!

ここまで書くことができたらいったんシミュレーターで実行してみましょう。(実機でも動作します。)

ちなみに、command + Rでも実行することができます。

応用する方法

他のパーツと応用して見た目を整えたり、さらに画面遷移することもできます

NavigationViewとの併用

NavigationView を間に挟むことにより、モーダル遷移した後に横方向にも遷移することができます。(プッシュ遷移)

struct ContentView: View {
    @State private var isShown: Bool = false
    var body: some View {
        Button(action: { self.isShown = true }) {
            Text("画面遷移する")
        }
        .sheet(isPresented: self.$isShown) {
            NavigationView {
                NavigationLink(destination: /*プッシュ遷移するビュー*/ AnotherView()) {
                    Text("プッシュ遷移する")
                }
            }
        }
    }
}

//遷移先のビュー
struct AnotherView: View {
    var body: some View {
        Text("プッシュ遷移されました。")
    }
}

Formとの併用

sheetをForm、NavigationViewと併用して、カレンダーのイベント追加画面のようなUIを再現することも可能になります。

struct ContentView: View {
    @State private var isShown: Bool = false
    var body: some View {
        Button(action: { self.isShown = true }) {
            Text("画面遷移する")
        }
        .sheet(isPresented: self.$isShown) {
            NavigationView {
                Form {
                    //ビュー
                    Text("フォームの中に画面遷移")
                    NavigationLink(destination: /*プッシュ遷移するビュー*/AnotherView()) {
                        //タップするとプッシュ遷移するビュー
                        Text("プッシュ遷移")
                    }
                }
            }
        }
    }
}

//遷移先のビュー
struct AnotherView: View {
    var body: some View {
        Text("プッシュ遷移されました。")
    }
}

遷移先の画面でシートを下げる・閉じる方法

struct ContentView: View {
    @State private var isShown: Bool = false
    var body: some View {
        Button(action: { self.isShown = true }) {
            Text("画面遷移する")
        }
        .sheet(isPresented: self.$isShown) {
            Button(action: {
                self.isShown = false
                //トリガーのプロパティ(変数、値)をtrueからfalseにすることで、シートを下げることができます。
            }) {
                Text("シートを下げる")
            }
        }
    }
}

sheetを閉じたときに処理を行う方法

onDismiss: {}という引数(パラメータ)を追加します。

その中に、シートを閉じたときの処理を書きます。

.sheet(isPresented: self.$isShown, onDismiss: {
    // 画面を閉じたときの処理...
    // 例: print("画面を閉じた!")
}) {
    Text("画面遷移")
}

また以下のような方法もあります。

こちらは、hapticFeedBack()という引数、戻り値なしのメソッド(関数)を定義し、

.sheet(isPresented: self.$isShown, onDismiss: hapticFeedBack) {}

という形で()をつけずにそのままメソッド名を書きます。

var body: some View {
Text("画面遷移する")
    .sheet(isPresented: self.$isShown, onDismiss: hapticFeedBack) {
        Text("画面遷移")
    }
}

func hapticFeedBack() {
    // 触覚フィードバック
    let generator = UINotificationFeedbackGenerator()
    generator.notificationOccurred(.success)
}

このコードを実行したタイミングで、端末がブルっと振動する、触覚フィードバックを実行することができます。(iPadや一部のiPhoneでは実行できないことがあります。)

まとめ・ソースコード

sheetを覚えるだけで、作れるアプリのUIの幅がグッと広がります!
ぜひご自分のアプリで使用してみてください!

最後まで読んでくださりありがとうございます!

sheetの基本的な使い方を網羅したコードを置いておきます。


import SwiftUI

struct ContentView: View {
    // when this variable is true, the modal View appears.
    @State private var show: Bool = false
    
    // when the modal view disappear(dismiss), message will appear.
    @State private var message: String = ""
    
    var body: some View {
        VStack {
            Button(action: {
                self.show.toggle()
            }) {
                Text("モーダル遷移")
            }
            .sheet(isPresented: self.$show, onDismiss: {
                // Dismiss したときの処理
                self.message = "Dismiss!"
            }) {
                SecondView()
            }
            Text("\(self.message)")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


struct SecondView: View {
    @Environment(\.presentationMode) var presentationMode
    var body: some View {
        NavigationView {
            Form {
                NavigationLink(destination: Text("Test!")) {
                    Text("プッシュ遷移")
                }
            }.navigationBarItems(trailing: Button(action: {
                // ボタンで閉じる場合
                self.presentationMode.wrappedValue.dismiss()
            }) {
                Text("閉じる")
            })
        }
    }
}

taichi_dm_

taichi_dm_

N高等学校の2年生です。人に価値を提供するプログラマーを目指して勉強中です。SwiftUIという言語を使うiOSアプリ開発が得意です!

FOLLOW

カテゴリー:
関連記事

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です