I've been messing around with trying to make custom expandable/collapsible views in SwifUI. Below is the code for a view that says "Tap Me" when "collapsed," and when tapped, expands to show each item
in items
, along with a button that allows the user to add items. I ended up getting something that looks like this when I display three such views in a List:
Now obviously, this is a horrendous animation. The view container occupies the needed space, then the actual view moves to the middle and then expands both upward and downward. Also, the animation is clippy when collapsing the view. With an expandable/collapsible view, my goal is for the expanded/conditional part of the view to cleanly "drop down" from the "tap me"/nonconditional part of the view. In other words, the "tap me" part should have a static position, rather than moving to the middle and going back up, and the expanded part of the view should slide down from the top. Where am I going wrong with this implementation and how can I fix it?
Here's the code for the collapsible view:
struct Collapsible: View {
@State private var isExpanded: Bool = false
@State private var items: [String] = ["item1", "item2", "item3"]
var body: some View {
VStack {
HStack {
Text("Tap Me")
Spacer()
Text("Info")
}
.contentShape(Rectangle())
.onTapGesture {
withAnimation(.spring()) {
isExpanded.toggle()
}
}
if isExpanded {
VStack {
ForEach(items, id: \.self) { item in
HStack {
Text("Item")
Spacer()
Text("Info")
}
.padding(.vertical, 5)
}
Button("Add Item") {
items.append("new item")
}
}
}
}
.clipped()
.background(Color.cyan)
}
}
I also have some very basic code to display these Collapsible()
views in a List. It is important to note that I opted to use if isExpanded{...}
in the code above rather than a .frame(...)
modifier to expand the view, because with the latter option, the list occupies extra space when collapsed if the user adds to items
. Therefore, I want my implementation to either continue to use the if isExpanded{..}
solution, unless someone can fix this issue: SwiftUI: List of subviews with variable height taking extra space
struct ContentView: View {
var body: some View {
List {
Collapsible()
Collapsible()
Collapsible()
}
}
}