LoginSignup
4
3

More than 1 year has passed since last update.

【SwiftUI】@Environmentを理解したい

Posted at

はじめに

今回は@Environmentのアウトプットとして書きます。

今回作成するもの

ボタンを押すとピンクの画面が表示され、閉じるボタンを押すとピンクの画面が閉じるような簡単なものを例に作成してみます。

Simulator-Screen-Recording-iPhone-14-Pro-2023-04-10-at-07.19.27.gif

今回は@Environmentを使用する前に、以前にやった@Bindingを用いて画面の切り替えをまずは行なってみます。

@Bindingを用いて作成する

まずは初めの白い画面を作成します。

struct ContentView: View {
    @State var isShow = false
    var body: some View {
        Button(action: {
            isShow = true
        }, label: {
            Text("画面を表示")
                .font(.largeTitle)
        })
        .fullScreenCover(isPresented: $isShow) {
            SecondView(isPresented: $isShow)
        }
    }
}

ピンクの画面(SecondView)が表示するかどうかを管理するisShowプロパティを用意します。
ButtonをタップするとSecondViewを表示させるのでButton actionisShow = trueとします。
fullScreenCoverisPresented引数の真偽値によって画面が表示されるかどうか決まり、content引数はクロージャでContentを返すので、ここに表示させたいView(SecondView)を書きます。

struct SecondView: View {
    @Binding var isPresented: Bool
    var body: some View {
        NavigationView {
            VStack {
                Text(isPresented ? "表示されている" : "非表示になった")
                    .font(.largeTitle)
                    .foregroundColor(.green)
                Button(action: {
                    isPresented = false
                }, label: {
                    Text("閉じる")
                        .font(.largeTitle)
                })
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(.pink)
        }
    }
}

今回の例ではSecondViewからContentViewに閉じるボタンがタップされたらContentViewisShowfalseにしてSecondViewを閉じなければならないので、SecondViewisPresentedContentViewisShow@Bindingによって結び、isPresentedが変更されたらその変更を参照させているisShowに知らせます。
Button actionisPresented = falseとします。

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
        SecondView(isPresented: Binding.constant(false))
    }
}

また、プレビューでもSecondViewを表示させたいのでContentView_PreviewsSecondView(isPresented: Binding.constant(false))を追加します。
Binding.constant(false)とすることでSecondView isPresentedfalseを初期値として入れることができます。

@Environmentを使って書き換える

ここから本題ですが、先ほどの@Bindingを使って作成したものを@Environmentを使って書き換えます。
iOS14とiOS15以降で変更された箇所があるので両方記載してみます。

iOS14の場合

struct SecondView: View {
    @Environment(\.presentationMode) var presentation
    var body: some View {
        NavigationView {
            VStack {
                Text(presentation.wrappedValue.isPresented ? "表示されている" : "非表示になった")
                    .font(.largeTitle)
                    .foregroundColor(.green)
                Button(action: {
                    presentation.wrappedValue.dismiss()
                }, label: {
                    Text("閉じる")
                        .font(.largeTitle)
                })
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(.pink)
        }
    }
}

@Bindingの部分を@Environment(\.presentationMode) var presentationに変更します。これは、@Environmentを用いてEnvironmentValuesの一つであるpresentationModeにアクセスする変数presentaionを定義しています。
この変数を使うことで画面の状態(presentation.wrappedValue.isPresented)や画面を閉じる処理(presentation.wrappedValue.dismiss())を扱うことができます。
wrappedValueでラップされている内部変数を使ってisPresenteddismiss()で処理することができます。

struct ContentView: View {
    @State var isShow = false
    var body: some View {
        Button(action: {
            isShow = true
        }, label: {
            Text("画面を表示")
                .font(.largeTitle)
        })
        .fullScreenCover(isPresented: $isShow) {
            SecondView()
        }
    }
}

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

ContentViewContentView_PreviewsでそれぞれSecondViewをインスタンス化している部分はそれぞれ@Bindingから@Environmentに変更されたので初期値を入れる必要はなくなりました。

iOS15以降の場合

先ほどのpresentaionModeを使用する方法は残念ながらiOS14から非推奨になってしまいました。こちらではiOS15からの方法を書いてみます。

struct SecondView: View {
//    @Environment(\.presentationMode) var presentation
    @Environment(\.isPresented) var isPresented
    @Environment(\.dismiss) var dismiss
    var body: some View {
        NavigationView {
            VStack {
                Text(isPresented ? "表示されている" : "非表示になった")
                    .font(.largeTitle)
                    .foregroundColor(.green)
                Button(action: {
                    dismiss()
                }, label: {
                    Text("閉じる")
                        .font(.largeTitle)
                })
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(.pink)
        }
    }
}

ContentViewContentView_Previewsに変更はありません。
SecondView@EnvironmentEnvironmentValuesisPresenteddismissになっています。

@Environment(\.isPresented) var isPresented
@Environment(\.dismiss) var dismiss

さらに、presentation.wrappedValue.isPresentedisPresentedになり、presentation.wrappedValue.dismiss()dismiss()とシンプルになっています。

ちなみに、dismiss()の部分はdismiss.callAsFunction()としても同じようです。

iOS14までのpresentationModeからもともと提供されていたisPresenteddismissですが、iOS15からはEnvironmentValuesとして扱いやすくなりました。

こちらのドキュメントにisPresenteddismiss以外のEnvironmentValuesが載っているので一度見てみても面白いかもしれません。

おわりに

SwiftUI学習始めたばかりなので、間違ってる点がありましたらTwitterまたはコメントで"やさしく"教えてくださいませ。

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3