Navigationで次の画面を表示した場合、通常は画面左上の「<Back」をクリックして戻ると思います。ただ、クリックせずにある条件が成立した時だけ戻りたい時もあると思います。
UIKitではUIViewControllerの popViewController メソッドを使用することで実現できました。これと同じことをSwiftUIでやってみたいと思います。
実現する方法には二通りあります。
- @Environment(.presentationMode)による方法
- @State と @Binding による方法
どちらも以下のような挙動を実現することができます。
コードで前画面へ戻る方法_その①:@Environment(.presentationMode)
まずは、@Environment(.presentationMode) による手順です。
遷移元は以下の通りです。NavigationLink に Text を設定しタップすると SubView に遷移します。UIKitで言うところの pushViewController と同じことをしています。
struct MainView: View {
    
    var body: some View {
        NavigationView {
            NavigationLink(destination: SubView()) {
                Text("Forward to SubView.")
            }
        }
    }
}続いて、遷移先画面ですが、まず、@EnvironmentプロパティのpresentationModeの変数を宣言します。
@Environmentは環境変数を操作する物と考えて良いかと思います(違っていたらご指摘下さい)。
今回は画面遷移を司どるpresentationModeを使用し、「<Back」を押した時と同じ挙動を実現します。
struct SubView: View {
    
    @Environment(\.presentationMode) var presentation
    
    var body: some View {
        Button(action: {
            self.presentation.wrappedValue.dismiss()
        }, label: {
            Text("Back to MainView.")
        })
    }
}@Environment(\.presentationMode)で環境変数をpresentationとして宣言しSubViewに紐付けています。
そして、ボタンを押した時に、self.presentation.wrappedValue.dissmiss()を実行し、遷移元の画面を戻ることができます。
実行結果は以下のような感じです。
presentationMode以外の環境変数については、こちらの公式リファレンスを参照してください。
コードで前画面へ戻る方法_その②:@State と @Binding
2つ目の方法は、NavigationLink(画面遷移) の発動を @State の Bool変数で管理し、その変数を遷移先の @Binding の Bool変数に紐づける方法です。
まずは遷移元のコードです。
struct MainView: View {
    
    @State private var isShowSubView = false
    
    var body: some View {
        NavigationView {
            VStack {
                Button(action: {
                    self.isShowSubView = true
                }, label: {
                    Text("Forward to SubView.")
                })
                
                NavigationLink(destination: SubView(isShowSubView: $isShowSubView), isActive: $isShowSubView) {
                    EmptyView()
                }
            }
        }
    }
}@State で宣言した isShowSubView を NavigationLink の isActive: Binding<Bool> に紐づけており、 Button のタップ時に true にすることで画面遷移が発生します。その際、遷移先に指定した SubView のイニシャライザーにも $isShowSubView として渡しています。
続いて遷移先のコードです。
struct SubView: View {
    
    @Binding var isShowSubView: Bool
    
    var body: some View {
        Button(action: {
            self.isShowSubView = false
        }, label: {
            Text("Back to MainView.")
        })
    }
}先ほどの遷移元からのイニシャライザー呼び出しを通して、@Binding で宣言した isShowSubView と遷移元の変数が紐づいています。
ボタンをタップした際に isShowSubView を false とすることで遷移元の NavigationLink 発動が解除され、結果として前の画面へ戻る挙動が発生します。
以上、「戻るボタンを使わずにコードによって前画面へ戻る方法」を紹介しました。
関連する記事として以下もご興味ありましたらご覧下さい。
以上




記事ありがとうございます!助かりました。
isShowSubViewは必要でしょうか?遷移先にある、遷移元に戻るボタンにself.presentation.wrappedValue.dismiss()をつけるだけで良さそうに思えました。
ひろさわさん
コメントありがとうございます。おっしゃる通り、Button と isShowSubView のフラグはいりませんね(NavigationLink 自体が Button の一種なわけですし)。
コードと記事内容の修正をしました。
また、ブログ開始からスパムコメントばかりで本当のコメント頂けて大変嬉しいです。これを励みに記事のクオリティを上げて発信を続けていこうと思います。
記事を拝見させていただきました。
SwiftUI初心者なので、とても参考になりました!
1点質問があります。もしご存知であれば教えていただきたいです。
dismissを実行して遷移元の画面に戻る際にアニメーションがデフォルトでついていると思うのですが、このアニメーションを無効化することは可能でしょうか。
遷移元から遷移先へのアニメーションは、下記のようにトランザクションを使って無効にしているのですが、同じようにdismissにトランザクションを指定してもアニメーションは残ったままでした。
transaction.disablesAnimations = true // 画面遷移アニメーション無効
withTransaction(transaction) {
(画面遷移実行処理)
}
下記のサイトも参考に試してみたのですが、実装方法が理解できない状態です。
https://dev.classmethod.jp/articles/hosting-controller-dismiss-false/
お手数ですが、もしご存知であれば教えていただけますと幸いです。。。