I'm trying to build a view with several collapsed lists, only the headers showing at first. When you tap on a header, its list should expand. Then, with the first list is expanded, if you tap on the header of another list, the first list should automatically collapse while the second list expands. So on and so forth, so only one list is visible at a time.
The code below works great to show multiple lists at the same time and they all expand and collapse with a tap, but I can't figure out what to do to make the already open lists collapse when I tap to expand a collapsed list.
Here's the code (sorry, kind of long):
import SwiftUI
struct Task: Identifiable {
let id: String = UUID().uuidString
let title: String
let subtask: [Subtask]
}
struct Subtask: Identifiable {
let id: String = UUID().uuidString
let title: String
}
struct SubtaskCell: View {
let task: Subtask
var body: some View {
HStack {
Image(systemName: "circle")
.foregroundColor(Color.primary.opacity(0.2))
Text(task.title)
}
}
}
struct TaskCell: View {
var task: Task
@State private var isExpanded = false
var body: some View {
content
.padding(.leading)
.frame(maxWidth: .infinity)
}
private var content: some View {
VStack(alignment: .leading, spacing: 8) {
header
if isExpanded {
Group {
List(task.subtask) { subtask in
SubtaskCell(task: subtask)
}
}
.padding(.leading)
}
Divider()
}
}
private var header: some View {
HStack {
Image(systemName: "square")
.foregroundColor(Color.primary.opacity(0.2))
Text(task.title)
}
.padding(.vertical, 4)
.onTapGesture {
withAnimation {
isExpanded.toggle()
}
}
}
}
struct ContentView: View {
//sample data
private let tasks: [Task] = [
Task(
title: "Create playground",
subtask: [
Subtask(title: "Cover image"),
Subtask(title: "Screenshots"),
]
),
Task(
title: "Write article",
subtask: [
Subtask(title: "Cover image"),
Subtask(title: "Screenshots"),
]
),
Task(
title: "Prepare assets",
subtask: [
Subtask(title: "Cover image"),
Subtask(title: "Screenshots"),
]
),
Task(
title: "Publish article",
subtask: [
Subtask(title: "Cover image"),
Subtask(title: "Screenshots"),
]
),
]
var body: some View {
NavigationView {
VStack(alignment: .leading) {
ForEach(tasks) { task in
TaskCell(task: task)
.animation(.default)
}
Spacer()
}
}
}
}
Thanks ahead for any help!
EDIT: Here's the collapse functionality to go with the accepted solution below:
Update the onTapGesture in private var header: some View to look like this:
.onTapGesture {
withAnimation {
if task.isExpanded {
viewmodel.collapse(task)
} else {
viewmodel.expand(task)
}
}
}
Then add the collapse function to class Viewmodel
func collapse(_ taks: TaskModel) {
var tasks = self.tasks
tasks = tasks.map {
var tempVar = $0
tempVar.isExpanded = false
return tempVar
}
self.tasks = tasks
}
That's it! Fully working as requested!