🍿

popover, sheetのisPresentedを子Viewにバインドせず(Environment Valuesを)使うとスッキリ書ける

2021/11/30に公開

概要

親のViewで childViewIsPresentedでpopoverで表示する子View ChildView の表示非表示を切り替えているとする。(下のコード参照)

そのときEnvironment Valuesを使うことで、ChildViewchildViewIsPresentedをバインドしなくても、ChildViewからchildViewIsPresetedが読み取れ、またChildViewのdismissが可能である。

Environment Valuesとは、システム定義のEnvironmentObjectのようなものだと思っておけば良いと思う。

使うやつ

isPresented | Apple Developer Documentation
dismiss | Apple Developer Documentation

サンプルコード

Before

ParentView

@State private var childViewIsPresented: Bool = false
var body: some View {
	...
	ToolbarItem(...) {
		Button("子Viewを開く") {
			childViewIsPresented  = true
		}
		.popover(isPresented: $childViewIsPresented, attachmentAnchor: .point(.top), arrowEdge: .top) {
			ChildView(childViewIsPresented: $childViewIsPresented)
		}
	}
	...
}

ChildView

childViewIsPresentedにtrue, falseを代入することで、ChildViewを含むpopoverの表示非表示を切り替える。

struct ChildView: View {
	@Binding var childViewIsPresented: Bool
	...
}

After

ParentView

ChildViewにchildViewIsPresentedをバインドしなくてよい。

var body: some View {
	...
	ToolbarItem(...) {
		Button(...)
			.popover(isPresented: $childViewIsPresented, attachmentAnchor: .point(.top), arrowEdge: .top) {
				ChildView()
			}
		}
	}
	...

ChildView

このようにpopoverの引数に渡したisPresentedを使うことができる。

struct ChildView: View {
	@Environment(\.isPresented) var childViewIsPresented: Bool
	...
}

また以下のコードでは、dismiss.callAsFunction()でpopoverを閉じることができる。

struct ChildView: View {
	@Environment(\.isPresented) var childViewIsPresented: Bool
	@Environment(\.dismiss) var dismiss: DismissAction
	...
}

Discussion