Due to constraints of using the default DisclosureGroup in a list, I'm trying to make a custom implementation, but I'm running into an issue with the expansion animation, which only occurs when the custom component is in a List. Here is the code:
struct CustomDisclosureGroup<Label: View, Content: View>: View {
@State private var isExpanded: Bool = false
private let label: Label
private let content: Content
init(@ViewBuilder label: () -> Label, @ViewBuilder content: () -> Content) {
self.label = label()
self.content = content()
}
var body: some View {
VStack(spacing: 0) {
Button {
withAnimation {
isExpanded.toggle()
}
} label: {
HStack {
label
Spacer()
}
}
if isExpanded {
content
}
}
}
}
However, when I put it in a List, as so:
struct ContentView: View {
@State var items: [Int] = [0, 1, 2, 3]
var body: some View {
List {
ForEach(items, id: \.self) { item in
CustomDisclosureGroup {
Text(item.formatted())
} content: {
Text("Test text")
Text("Test text")
Text("Test text")
}
}
}
}
}
The animation breaks. Compare it to the default disclosure group animation:
| With Custom Component | Default Disclosure Group |
|---|---|
![]() |
![]() |
Assume that I cannot use DisclosureGroup (and need to use this custom component) and that I must use List. How can I fix this broken animation?


Animations inside a
Listare very restricted and difficult to control, as you have discovered. The same goes for animations inside aForm.Here is an updated version of your
CustomDisclosureGroupthat performs slightly better than what you originaly had, but not by much.withAnimation, because this was causing some uncontrollable movement (including movement of the label). Instead, an.animationmodifier is applied when revealing the content.defaultMinListRowHeight) and the height of the label by itself when the view is collapsed.If you want to have more control over the animation, you might be better off building a custom
Listinstead. This would probably be easier than finding workarounds for all the limitations of a nativeList.