はじめに
今回は@Environmentのアウトプットとして書きます。
今回作成するもの
ボタンを押すとピンクの画面が表示され、閉じるボタンを押すとピンクの画面が閉じるような簡単なものを例に作成してみます。
今回は@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 actionでisShow = trueとします。
fullScreenCoverのisPresented引数の真偽値によって画面が表示されるかどうか決まり、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に閉じるボタンがタップされたらContentViewのisShowをfalseにしてSecondViewを閉じなければならないので、SecondViewのisPresentedとContentViewのisShowを@Bindingによって結び、isPresentedが変更されたらその変更を参照させているisShowに知らせます。
Button actionでisPresented = falseとします。
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
SecondView(isPresented: Binding.constant(false))
}
}
また、プレビューでもSecondViewを表示させたいのでContentView_PreviewsにSecondView(isPresented: Binding.constant(false))を追加します。
Binding.constant(false)とすることでSecondView isPresentedにfalseを初期値として入れることができます。
@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でラップされている内部変数を使ってisPresentedやdismiss()で処理することができます。
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()
}
}
ContentViewとContentView_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)
}
}
}
ContentViewとContentView_Previewsに変更はありません。
SecondViewの@EnvironmentのEnvironmentValuesがisPresentedとdismissになっています。
@Environment(\.isPresented) var isPresented
@Environment(\.dismiss) var dismiss
さらに、presentation.wrappedValue.isPresentedはisPresentedになり、presentation.wrappedValue.dismiss()はdismiss()とシンプルになっています。
ちなみに、dismiss()の部分はdismiss.callAsFunction()としても同じようです。
iOS14までのpresentationModeからもともと提供されていたisPresentedとdismissですが、iOS15からはEnvironmentValuesとして扱いやすくなりました。
こちらのドキュメントにisPresentedやdismiss以外のEnvironmentValuesが載っているので一度見てみても面白いかもしれません。
おわりに
SwiftUI学習始めたばかりなので、間違ってる点がありましたらTwitterまたはコメントで"やさしく"教えてくださいませ。
