I have a DisclosureGroup
which when expanded, has a Button
that presents a .sheet
. However, when the .sheet
is presented the first time, it is immediately dismissed with an error to the console:
Attempt to present <TtGC7SwiftUI29PresentationHostingControllerVS_7AnyView: 0x11c210700> on <TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier_: 0x104188600> (from <TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier_: 0x104188600>) while a presentation is in progress.
Here is the code:
struct HomeView: View
{
var body: some View
{
List
{
CompositedTestDisclosureGroup()
}
}
}
struct CompositedTestDisclosureGroup: View
{
@State private var disclosureGroupIsExpanded = false
@State private var sheetIsPresented = false
var body: some View
{
TestDisclosureGroup(disclosureGroupIsExpanded: self.$disclosureGroupIsExpanded)
{
self.sheetIsPresented = true
}
.sheet(isPresented: self.$sheetIsPresented)
{
Text("Test")
}
}
}
struct TestDisclosureGroup: View
{
@Binding var disclosureGroupIsExpanded: Bool
var presentSheet: () -> Void
var body: some View
{
DisclosureGroup("Disclosure Group", isExpanded: self.$disclosureGroupIsExpanded)
{
Button("Push Me")
{
presentSheet()
}
.buttonStyle(.borderedProminent)
}
}
}
I know if I remove the outer List in HomeView
,remove the DisclosureGroup
and just have the Button
by itself or have the .sheet
presented in HomeView
, it will work but I'd like to try and keep the design the way it is.
Any insight into why this is happening is much appreciated, thanks.
When a DisclosureGroup
is shown inside a List
, it seems it is just like a Group
. In other words, it behaves like a collection of views, rather than like a container.
In this context, applying .sheet
as modifier to DisclosureGroup
(or to the wrapper view TestDisclosureGroup
) is like adding it separately to the label and to the content. When the flag sheetIsPresented
is set to true, both sheets try to show simultaneously, hence the error.
One way to fix is to wrap the DisclosureGroup
with a container and apply the .sheet
to the container instead. It's interesting to try a ZStack
as container:
ZStack {
TestDisclosureGroup(disclosureGroupIsExpanded: $disclosureGroupIsExpanded) {
sheetIsPresented = true
}
}
.sheet(isPresented: $sheetIsPresented) {
Text("Test")
}
When the DisclosureGroup
is expanded, it looks like this:
This illustrates how the DisclosureGroup
behaves like a group of views. It also demonstrates, that ZStack
is not a good choice of container.
A VStack
works a bit better, but the animation is not good.
So a better way to fix is to move the .sheet
modifier to the Button
inside the DisclosureGroup
, or to the Label
(by using a different initializer for DisclosureGroup
), or to the parent List
. This of course means changing the way you are passing the bindings and closures around.
Here is how it can be moved to the List
:
struct HomeView: View {
@State private var sheetIsPresented = false
var body: some View {
List {
CompositedTestDisclosureGroup(sheetIsPresented: $sheetIsPresented)
}
.sheet(isPresented: $sheetIsPresented) {
Text("Test")
}
}
}
struct CompositedTestDisclosureGroup: View {
@State private var disclosureGroupIsExpanded = false
@Binding var sheetIsPresented: Bool
var body: some View {
TestDisclosureGroup(disclosureGroupIsExpanded: $disclosureGroupIsExpanded) {
sheetIsPresented = true
}
}
}