【SwiftUI】戻るボタンをカスタマイズする方法

NavigationLink で画面遷移すると自動的に次の画面の左上に<戻るボタンが付加されます。

しかし、世の様々なアプリではボタンの形状や色をアプリデザインに合わせてカスタマイズしている例をよく見ます。

今回はその戻るボタンのカスタマイズ方法について紹介していこうと思います。

※尚、SwiftUI 3.0 を前提とした内容となります。

動作確認済環境

macOS Big Sur 11.6

Xcode 13.1

iOS 15.0

iPhone 13 mini シミュレータ

【SwiftUI】戻るボタンをカスタマイズする方法

toolbar と ToolbarItem

SwiftUI 2.0 までは navigationBarItems というプロパティがありましたが 3.0 で deprecated(非推奨) となりました。

代わりに、toolbar プロパティとその中身となる ToolbarItem を使ってOSの戻るボタンの場所にカスタムしたボタンを配置します。

ToolbarItem(placement: ToolbarItemPlacement)

ToolbarItem の placement 引数に navigationBarLeading を指定することで画面左上にボタンを配置できます。

.toolbar {
    ToolbarItem(placement: .navigationBarLeading) {
        Button(...)
    }
}

ちなみに navigationBarTrailing と bottomBar というものもありますが今回は触れません。

サンプルコード

遷移元
struct FirstView: View {
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink("Go Next", destination: SecondView())
            }
            .navigationTitle("First View")
        }
    }
}
遷移先(戻るボタンを配置)
struct SecondView: View {
    
    @Environment(\.dismiss) var dismiss
    
    var body: some View {
        VStack {
            Text("Second View")
                .padding()
        }
        .navigationTitle("Second View")
        .navigationBarBackButtonHidden(true)
        .toolbar {
            ToolbarItem(placement: .navigationBarLeading) {
                Button(
                    action: {
                        dismiss()
                    }, label: {
                        Image(systemName: "arrow.backward")
                    }
                ).tint(.orange)
            }
        }
    }
}

ToolbarItem の中身である Button の action で環境変数の dismiss() をコールすることで前の画面へ戻れます。

また、navigationBarBackButtonHidden(true)OSの戻るボタンを非表示にすることを忘れないようにしましょう。

ViewModifier化する

戻るボタンを表示したい画面全てで同じコードは書きたくないので、ViewModifier を継承してどこでも使えるようにしてみましょう。

CustomBackButton

ViewModifier を継承した CustomBackButton を作り処理を内包します。

struct CustomBackButton: ViewModifier {
    
    @Environment(\.dismiss) var dismiss

    func body(content: Content) -> some View {
        content
            .navigationBarBackButtonHidden(true)
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    Button(
                        action: {
                            dismiss()
                        }, label: {
                            Image(systemName: "arrow.backward")
                        }
                    ).tint(.orange)
                }
            }
    }
}

View の extension に追加

そして、View の extension の定義に customBackButton というプロパティを追加してシンプルに使えるようにします。

extension View {
    func customBackButton() -> some View {
        self.modifier(CustomBackButton())
    }
}
使用例
struct SecondView: View {
    var body: some View {
        VStack {
            Text("Second View")
                .padding()
        }
        .navigationTitle("Second View")
        .customBackButton()
    }
}

先程よりだいぶスッキリしましたね。

以上、参考になりましたら嬉しいです。