1

I'm working with a project in SwiftUI. I want to create something like,

enter image description here

The code I'm using currently:

TaskListView

struct TaskListView: View {
    var tasks: [Task] = Task.all()
    
    var body: some View {
        List {
            ForEach(self.tasks) {task in
                TaskView(task: task)
            }
        }
    }
}

TaskView

struct TaskView: View {
    @ObservedObject var task: Task
    
    var body: some View {
        VStack {
            Text(task.name)
            .font(.custom("Avenir Next Regular", size: 14))
            
            //Here.................
            if !task.subtasks.isEmpty {
                Section {
                    ForEach(task.subtasks) {subtask in
                        TDTaskView(task: subtask)
                    }
                }.padding(.leading)
            }
        }
    }
}

Task model:

class Task: Identifiable, ObservableObject {
    var id: UUID = UUID()
    var name: String
    @Published var isCompleted: Bool = false
    var subtasks = [Task]()
    
    init(name: String, isCompleted: Bool = false, subtasks: [Task] = [Task]()) {
        self.name = name
        self.isCompleted = isCompleted
        self.subtasks = subtasks
    }
}

The way I'm trying to implement nested List is now working properly on selection. Am I implementing it wrongly?

2
  • You need to use sections. This SwiftUI - nested list can be helpful. Commented Jul 21, 2020 at 11:21
  • 1
    I tried this already. But the issue is I don't know the nesting levels. In the link example, its just 2 level nesting. Commented Jul 21, 2020 at 11:23

2 Answers 2

1

You may try to create items recursively:

struct ContentView: View {
    var tasks: [Task] = Task.all()

    var body: some View {
        List {
            TaskListView(tasks: tasks)
        }
    }
}
struct TaskListView: View {
    var tasks: [Task]

    var body: some View {
        ForEach(tasks, id: \.id) { task in
            TaskView(task: task)
        }
    }
}
struct TaskView: View {
    @ObservedObject var task: Task

    var body: some View {
        VStack {
            HStack {
                Circle().stroke() // replace with a custom control
                    .frame(width: 20, height: 20)
                Text(task.name)
                    .font(.custom("Avenir Next Regular", size: 14))
                Spacer()
            }
            if !task.subtasks.isEmpty {
                TaskListView(tasks: task.subtasks)
                    .padding(.leading)
            }
        }
    }
}
Sign up to request clarification or add additional context in comments.

Comments

0

You're very close, I'd say change only two things.

First, change the VStack to a Group, so that the individual "tasks" will be managed by the List:

var body: some View {
    VStack {
        Text(task.name)
        .font(.custom("Avenir Next Regular", size: 14))

var body: some View {
    Group {
        Text(task.name)
        .font(.custom("Avenir Next Regular", size: 14))

Second, change your TDTaskView to TaskView, so the code you already wrote will recursively build out your task list:

        if !task.subtasks.isEmpty {
            Section {
                ForEach(task.subtasks) {subtask in
                    TDTaskView(task: subtask)
                }
            }.padding(.leading)
        }

        if !task.subtasks.isEmpty {
            Section {
                ForEach(task.subtasks) {subtask in
                    TaskView(task: subtask)
                }
            }.padding(.leading)
        }

Edit

Here's the full working code (very close to your original code):

import SwiftUI

struct ContentView: View {
    var tasks: [Task] = [Task(name: "thing"), Task(name: "more work"), Task(name: "Huge job", isCompleted: false, subtasks: [Task(name: "smaller job"), Task(name: "sorta small job")])]
    
    var body: some View {
        List {
            ForEach(self.tasks) {task in
                TaskView(task: task)
            }
        }
    }
}

struct TaskView: View {
    @ObservedObject var task: Task
    
    var body: some View {
        Group {
            Text(task.name)
                .font(.custom("Avenir Next Regular", size: 14))
            
            //Here.................
            if !task.subtasks.isEmpty {
                Section {
                    ForEach(task.subtasks) {subtask in
                        TaskView(task: subtask)
                    }
                }.padding(.leading)
            }
        }
    }
}


class Task: Identifiable, ObservableObject {
    var id: UUID = UUID()
    var name: String
    @Published var isCompleted: Bool = false
    var subtasks = [Task]()
    
    init(name: String, isCompleted: Bool = false, subtasks: [Task] = [Task]()) {
        self.name = name
        self.isCompleted = isCompleted
        self.subtasks = subtasks
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.