I need to expand each row of a List
with a smooth animation (from top to bottom). A row has a main text (always displayed) and a secondary text that is shown only when row is expanded.
Here is code for List
:
struct ContentView: View {
var body: some View {
NavigationView {
List {
ForEach((1...3), id: \.self) { _ in
ItemRow()
}
}
.navigationTitle("Demo")
.navigationBarTitleDisplayMode(.inline)
}
}
}
And for ItemRow
:
struct ItemRow: View {
@State private var expanded = false
var body: some View {
VStack {
HStack {
Text("Hello world")
Spacer()
Image(systemName: "chevron.forward.circle")
.rotationEffect(.degrees(expanded ? 90 : 0))
.onTapGesture {
withAnimation() {
expanded.toggle()
}
}
}
if expanded {
Text("Lorem ipsum dolor sit amet. Vel dicta error qui vero incidunt et fugit quisquam aut modi praesentium qui veritatis sed ipsam magnam. Ad iure velit ut possimus voluptatem cum dolores dicta. Ex cupiditate libero ut impedit internos aut reprehenderit molestias! Aut debitis dignissimos sit incidunt internos aut mollitia explicabo aut vitae numquam et repellendus iusto.")
.foregroundColor(.secondary)
.font(.caption)
.frame(height: expanded ? nil : 0)
.clipped()
}
}
}
}
Rows expand when I tap on chevron button. But the animation is not smooth at all: the main text starts in center at first and moves on top of row.
Since DiscloureButtonStyle is iOS 16 Beta only, here is a trick to show custom chevron (I don't recommend this approach btw):
struct ItemRow: View {
@State private var expanded = false
var body: some View {
DisclosureGroup(isExpanded: $expanded) {
Text("Lorem ipsum dolor sit amet. Vel dicta error qui vero incidunt et fugit quisquam aut modi praesentium qui veritatis sed ipsam magnam. Ad iure velit ut possimus voluptatem cum dolores dicta. Ex cupiditate libero ut impedit internos aut reprehenderit molestias! Aut debitis dignissimos sit incidunt internos aut mollitia explicabo aut vitae numquam et repellendus iusto.")
.foregroundColor(.secondary)
.font(.caption)
.clipped()
} label: {
HStack {
Text("Hello world")
Spacer()
Image(systemName: "chevron.forward.circle")
.rotationEffect(.degrees(expanded ? 90 : 0))
}.padding(.trailing, -20) // <-- To use the space of the default one
}.accentColor(.clear) // <-- To hide the default one
}
}