Home > Net >  How to append something to a list?
How to append something to a list?

Time:03-07

I'm new to Swift at the moment and I'm having trouble appending to a to-do list. I am not receiving an error, so I don't know what's wrong. I hope this isn't too much code to go through, but I don't even know where the problem is.

There is a sheet modal that opens and closes fine. The only issue is that when I press the save button, the information I typed in doesn't add to the list.

I've add the ViewModel, NewTaskView(sheet), TaskView(customizes list), and a bit of code of the DetailView where the list should be.


import Foundation
import SwiftUI

class TaskViewModel : Identifiable , ObservableObject {
    
    @Published var tasks : [Task] = [
        Task(taskName: "Lab", taskDate: Date(), taskCompleted: false),
        Task(taskName: "Assignment 4.02", taskDate: Date(), taskCompleted: false)
        
    ]
    
    @Published var sortType : SortType = .alphabetical
    @Published var isPresented = false
    @Published var searched = ""
    
    func addTask(task : Task){
        tasks.append(task)
    }
    
    func removeTask(indexAt : IndexSet){
        tasks.remove(atOffsets: indexAt)
    }
    
    func sort(){
        
        switch sortType {
        case .alphabetical :
            tasks.sort(by: { $0.taskName < $1.taskName })
        case .date :
            tasks.sort(by: { $0.taskDate > $1.taskDate })
        }
    }

}

struct Task : Identifiable , Equatable {
    
    var id : String = UUID().uuidString
    let taskName : String
    let taskDate : Date
    var taskCompleted: Bool
    
}

enum SortType : String , Identifiable , CaseIterable {
    
    var id : String { rawValue }
    
    case alphabetical
    case date
}
    

struct NewTaskView: View {
    
    
    @Environment(\.presentationMode) var presentationMode
    
    @ObservedObject var taskVM : TaskViewModel
    
    @State var taskName = ""
    @State var taskCompleted = Bool()
    @State var taskDate = Date()
    
    
    var body: some View {
        NavigationView {
            VStack(spacing: 14) {
                Spacer()
                
                TextField("Assignment Name",text: $taskName)
                    .padding()
                    .background(Color("tan"))
                    .cornerRadius(5)
                    .font(Font.custom(FontNameManager.Montserrat.semibold, size: 15))
                    .padding(.bottom, 20)
                HStack{
                Text("Due Date")
                        .font(Font.custom(FontNameManager.Montserrat.semibold, size: 15))
                    Image(systemName: "bell.badge")
                    .foregroundColor(.gray)
                    .frame(width: 30, height: 30, alignment: .leading)
                VStack{
                    DatePicker("Select Date", selection: $taskDate, displayedComponents: .date)
                        .labelsHidden()
                }
                }
                HStack{
                    Text("Mark as Completed")
                        .font(Font.custom(FontNameManager.Montserrat.semibold, size: 15))
                    Button(action : {
                        
                        taskCompleted.toggle()
                        
                        
                        
                    }){
                        if taskCompleted == true {
                        Image(systemName: "checkmark.circle.fill")
                                .foregroundColor(Color("orange"))
                                .font(.title2)
                                
                        }
                        else {
                            Image(systemName: "circle")
                                .foregroundColor(Color("lightorange"))
                                .font(.title2)
                                
                                
                        }
                    }
                }
                Spacer()
                
                
                Button(action:{taskVM.addTask(task: .init(taskName: taskName, taskDate: taskDate, taskCompleted: taskCompleted))
                                    
                    presentationMode.wrappedValue.dismiss()},
                                   label:{
                                    Text("Save")
                                   })
                
            }.padding()
                .navigationBarItems(trailing: Button(action: {
                    presentationMode.wrappedValue.dismiss()
                }) {
                    HStack{
                        
                        Image(systemName: "xmark")
                            .foregroundColor(Color("orange"))
                            
                    }
                })
        }
    }
}
struct NewTaskView_Previews: PreviewProvider {
    static var previews: some View {
        NewTaskView(taskVM: TaskViewModel())
        
    }
}

struct TaskView: View {
    
    var task : Task
    
    @Environment(\.managedObjectContext) var moc
    
    @State var currentDate: Date = Date()
    @State var taskCompleted = false
    
    func getDateFormatString(date:Date) -> String
    {
        var dateString = ""
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "MMM. dd"
        
        dateString = dateFormatter.string(from: date)
        return dateString
    }
    
    var body: some View {
        HStack {
            Button(action : {
                
                self.taskCompleted.toggle()
                
                try? self.moc.save()
                
            }){
                if self.taskCompleted {
                Image(systemName: "checkmark.circle.fill")
                        .foregroundColor(Color("orange"))
                        .font(.title2)
                        
                }
                else {
                    Image(systemName: "circle")
                        .foregroundColor(Color("lightorange"))
                        .font(.title2)
                        
                        
                }
            }.padding()
            
            VStack (alignment: .leading, spacing: 2){
                Text("\(task.taskName)")
                .font(Font.custom(FontNameManager.Montserrat.bold, size: 18))
                .foregroundColor(Color("dark"))
                .padding([.leading, .trailing], 1)
                
                if self.taskCompleted {
                    Text("Completed")
                        .font(Font.custom(FontNameManager.Montserrat.bold, size: 12))
                        .foregroundColor(Color("burntorange"))
                        .frame(width: 95, height: 20)
                        .background(Color("lightorange"))
                        .cornerRadius(4)
                }
                
                else if currentDate > task.taskDate {
                    Text("Late")
                        .font(Font.custom(FontNameManager.Montserrat.semibold, size: 12))
                        .foregroundColor(.white)
                        .frame(width: 95, height: 18)
                        .background(Color("lightbrown"))
                        .cornerRadius(4)
                        
                }
                
                else {
                    let diffs = Calendar.current.dateComponents([.day], from: currentDate, to: task.taskDate )
                    
                    Text("\(diffs.day!) days left")
                        .font(Font.custom(FontNameManager.Montserrat.semibold, size: 12))
                        .foregroundColor(Color("burntorange"))
                        .frame(width: 95, height: 20)
                        .background(Color("lightorange"))
                        .cornerRadius(4)
                }
            }
            
            Spacer()
            
            Text(getDateFormatString(date: task.taskDate ))
                .font(Font.custom(FontNameManager.Montserrat.medium, size: 12))
                .padding()
            }
        .padding(10)
        .cornerRadius(10)
        .background(
            RoundedRectangle(cornerRadius: 10 , style: .continuous)
                .foregroundColor(.white))
    }
}

struct TaskView_Previews: PreviewProvider {
    static var previews: some View {
        TaskView(task: Task(id: "", taskName: "Task Name", taskDate: Date(), taskCompleted: false))
    }
}

struct DetailsView: View {
    @Environment(\.managedObjectContext) var moc
    
    @ObservedObject var developers : Developer
    @ObservedObject var taskVM : TaskViewModel
    
    @Environment(\.presentationMode) var presen
    @Environment(\.editMode) var editButton
    
    @State var taskCompleted = false
    @State var show = false
    @State var showSearch = false
    @State var txt = ""
    @State var currentDate: Date = Date()
    @State var image : Data = .init(count: 0)
    @State var indices : [Int] = []
    
    func getDateFormatString(date:Date) -> String
    {
        var dateString = ""
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "MMM. dd"
        
        dateString = dateFormatter.string(from: date)
        return dateString
    }
    
    var body: some View {
        NavigationView {
            ZStack {
            ZStack{
                VStack{
                    Color("lightorange")
                        .clipShape(CustomCorners(corners: [.bottomLeft, .bottomRight], size: 70))
                        .ignoresSafeArea(.all, edges: .top)
                        .frame(width: 420, height: 175)
                    Spacer()
                }
                
                VStack{
                    HStack {
                        
                        if !self.showSearch{
                        Button(action: {
                            
                            withAnimation(.spring()){
                                self.presen.wrappedValue.dismiss()
                            }
                        }) {
                            Image(systemName: "chevron.left")
                                .font(.title2)
                                .foregroundColor(Color("dark"))
                                .padding(10)
                        }
                        }
                        
                        Spacer(minLength: 0)
                       
                        HStack {

                            if self.showSearch{
                                
                                Image(systemName: "magnifyingglass")
                                    .font(.title2)
                                    .foregroundColor(Color("dark"))
                                    .padding(.horizontal, 8)
                                    
                                
                                TextField("Search", text: $taskVM.searched , onEditingChanged: { (isBegin) in
                                    if isBegin {
                                        showSearch = true
                                    } else {
                                        showSearch = false
                                    }
                                })
                                
                                Button(action: {
                                    withAnimation{
                                    self.showSearch.toggle()
                                    }
                                }) {
                                    
                                    Image(systemName: "xmark").foregroundColor(.black).padding(.horizontal, 8)
                                }
                            }
                            
                            else {
                        Button(action: {
                            withAnimation {
                                
                            self.showSearch.toggle()
                            
                        }
                        }) {
                            Image(systemName: "magnifyingglass")
                                .font(.title2)
                                .foregroundColor(Color("dark"))
                                .padding(10)
                        }
                        }
                        }.padding(self.showSearch ? 10 : 0)
                            .background(Color("lightorange"))
                            .cornerRadius(20)
                        
                    
                    }
                    ZStack{
                      RoundedRectangle(cornerRadius: 25)
                            .foregroundColor(Color("tan"))
                            .frame(width: 335, height: 130)
                        HStack {
                            VStack (alignment: .leading){
                               // Image(uiImage: UIImage(data: developers.imageD ?? self.image)!)
                               //     .resizable()
                               //   .frame(width: 70, height: 70)
                               //    .clipShape(RoundedRectangle(cornerRadius: 20))
                                
                                Text("\(developers.username ?? "")")
                                    .font(Font.custom(FontNameManager.Montserrat.bold, size: 24))
                                    .foregroundColor(Color("dark"))

                                Text("\(developers.descriptions ?? "")")
                                    .font(Font.custom(FontNameManager.Montserrat.semibold, size: 18))
                                    .foregroundColor(.gray)
                                
                            }
                            .padding([.leading, .trailing], 70)
                            .padding(.bottom, 80)
                            
                            Spacer()
                        }
                    }
                    
                    HStack{
                        Text("Assignments")
                            .font(Font.custom(FontNameManager.Montserrat.bold, size: 20))
                        
                        Spacer(minLength: 0)
                        
                        EditButton()
                   
                    }
                    .padding([.leading, .trailing], 20)
                    Spacer()
                    
                List {
                    ForEach (taskVM.tasks.filter {
                        self.taskVM.searched.isEmpty ? true : $0.taskName.localizedCapitalized.contains(self.taskVM.searched)} ){ task in
                            TaskView(task: task)
                                .listRowSeparator(.hidden)
            }
                    .onDelete(perform: {
                        taskVM.removeTask(indexAt: $0)
                    })
                }
                .listStyle(InsetListStyle())
                    
                }
            }
                VStack {
                    Spacer()
                    HStack {
                        Spacer()
                        ExpandableFAB(taskVM: TaskViewModel(), show: $show)
                    }
                }
                
            }.navigationBarHidden(true)
            .navigationBarBackButtonHidden(true)
        }
    }
}


struct DetailsView_Previews: PreviewProvider {
    static let persistenceController = PersistenceController.shared
    
    static var developers: Developer = {
                let context = persistenceController.container.viewContext
                let developers = Developer(context: context)
                developers.username = "Math"
                developers.descriptions = "Calculus"
                return developers
            }()
    
    static var previews: some View {
        
        DetailsView(developers: developers, taskVM: TaskViewModel()).environment(\.managedObjectContext, persistenceController.container.viewContext)
    }
}

struct ExpandableFAB: View {
    
    @Environment(\.managedObjectContext) var moc
    @ObservedObject var taskVM : TaskViewModel
    
    @Binding var show: Bool
    
    var body: some View{
        
        VStack(spacing: 20){
            
            if self.show{
                Button(action: {
                    taskVM.isPresented.toggle()
                }) {
                    
                    Image(systemName: "list.bullet.rectangle.portrait").resizable().frame(width: 25, height: 25).padding()
                    
                }
                .foregroundColor(.white)
                    .background(Color("orange"))
                    .clipShape(Circle())
                    
                
                Button(action: {
                    self.show.toggle()
                }) {
                    
                    Image(systemName: "doc.badge.plus").resizable().frame(width: 25, height: 25).padding()
                    
                }.foregroundColor(.white)
                    .background(Color("orange"))
                    .clipShape(Circle())
                    
            }
            
            Button(action: {
                self.show.toggle()
                
            }) {
                
                Image(systemName: "plus").resizable().frame(width: 25, height: 25).padding()
                
            }.foregroundColor(.white)
                .background(Color("orange"))
                .clipShape(RoundedRectangle(cornerRadius: 20))
                .padding()
                .rotationEffect(.init(degrees: self.show ? 180 : 0))
            
        }.fullScreenCover(isPresented: $taskVM.isPresented, content: {
            NewTaskView(taskVM: taskVM)
        })
        
    }
}

CodePudding user response:

@burnsi I don't think I have that anywhere. Where should there be one?

Of course you have one and you need one:

ExpandableFAB(taskVM: TaskViewModel(), show: $show)

This line is causing the issue.

You are somehow injecting a TaskViewModel in your DetailsView (You are not showing where), but not using it. Try:

ExpandableFAB(taskVM: taskVM, show: $show)

And the place you create your ViewModel ('TaskViewModel()') should have a @StateObject wrapper.

Explanation:

As you add something to your ViewModel the views depending on the ViewModel get rebuild. So your TaskViewModel ends up beeing recreated and you still have your 2 initial values in your array.

  • Related