代码之家  ›  专栏  ›  技术社区  ›  Xenomy

添加新任务会重复UITableView中的现有任务

  •  0
  • Xenomy  · 技术社区  · 1 年前

    我正在开发一款iOS应用程序,用户可以使用核心数据添加任务。我有一个TaskViewModel,它处理核心数据和UITableView之间的数据流。但是,我遇到了一个问题,添加新任务会复制表视图中的现有任务。

    以下是应用程序流的工作原理:

    当用户点击“添加任务”按钮时,它会显示一个AddNewTaskViewController。 在AddNewTaskViewController中,用户输入任务名称并点击“保存”。 然后通过TaskViewModel将输入的任务添加到核心数据中。 添加任务后,TaskViewModel会更新其任务数组,并通知UITableView重新加载其数据。

    尽管有这些步骤,但每当我添加新任务时,它都会复制表视图中的现有任务。例如,如果我添加“Test”作为第一个任务,然后添加“Test2”作为第二个任务,则表视图显示“Test,Test,Test2”。

    以下是添加新任务后问题的gif图: gif of the problem

    ViewController。敏捷的

    class ViewController: UIViewController {
        let viewModel = TaskViewModel.shared
        lazy var tasksTableView: UITableView = {
            let tableView = UITableView()
            tableView.translatesAutoresizingMaskIntoConstraints = false
            tableView.dataSource = self
            tableView.delegate = self
            tableView.register(TaskTableViewCell.self, forCellReuseIdentifier: "TaskTableCell")
           
            tableView.estimatedRowHeight = 200
            tableView.rowHeight = UITableView.automaticDimension
            return tableView
        }()
    
        lazy var addNewButton: UIButton = {
            let v = UIButton()
            v.translatesAutoresizingMaskIntoConstraints = false
            v.setTitle("Add Task", for: .normal)
            v.backgroundColor = .systemBlue
            v.layer.cornerRadius = 10
            v.frame = CGRect(x: 0, y: 0, width: 100, height: 35)
    
            v.addTarget(self, action: #selector(addPressed(sender: )), for: .touchUpInside)
            return v
        }()
    
        override func viewDidAppear(_ animated: Bool) {
    
            tasksTableView.reloadData()
        }
    
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
            view.backgroundColor = .systemBackground
            title = "Alisveris Listesi"
    
           navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "plus.circle.fill"), style: .done, target: self, action: #selector(showAddVC))
    
            navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .close,  target: self, action: #selector(deleteAllPressed(sender: )))
    
            setupViews()
        }
        private func setupViews() {
            view.addSubview(tasksTableView)
            configureConstraints()
        }
    
        private func configureConstraints() {
            NSLayoutConstraint.activate([
                tasksTableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 16),
                tasksTableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
                tasksTableView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
                tasksTableView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 16)
            ])
        }
    
        @objc func showAddVC() {
            let vc = AddNewTaskViewController()
            vc.delegate = self
            let navController = UINavigationController(rootViewController:  vc)
    
            navigationController?.present(navController, animated: true)
        }
    
        @objc func addPressed(sender: UIButton) {
            viewModel.addNewTask(name: "New Task")
        }
    
        @objc func getTaskPressed(sender: UIButton) {
            let tasks = CoreDataManager.shared.fetchAll()
            for task in tasks {
                print(task.name ?? "" )
            }
        }
    
        @objc func deleteAllPressed(sender: UIButton) {
            let tasks = CoreDataManager.shared.fetchAll()
            for task in tasks {
                CoreDataManager.shared.deleteItem(id: task.id ?? UUID() )
            }
    
            let fetchedTasks = CoreDataManager.shared.fetchAll()
            
            tasksTableView.reloadData()
            print(fetchedTasks.count)
    
        }
    
    }
    
    
    extension ViewController: UITableViewDelegate, UITableViewDataSource {
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return viewModel.numberOfRows(by: section)
        }
        
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    //        if indexPath.section == 0 {
    //            guard let cell = tableView.dequeueReusableCell(withIdentifier: "TaskTableCell", for: indexPath) as? TaskTableViewCell else {
    //                return UITableViewCell()
    //            }
    ////            let taskSummary =
    //            cell.configure(with: viewModel.tasks[indexPath.row])
    //            return cell
    //        }
    
            guard let cell = tableView.dequeueReusableCell(withIdentifier: "TaskTableCell", for: indexPath) as? TaskTableViewCell else {
                return UITableViewCell()
            }
            cell.configure(with: viewModel.tasks[indexPath.row])
            return cell
        }
    
        func numberOfSections(in tableView: UITableView) -> Int {
            return viewModel.numberOfTasks
        }
    
    
    }
    
    extension ViewController: ItemControllerDelegate {
        // Implement delegate methods
        func didItemAdded() {
    
            // Handle item added event
            tasksTableView.reloadData()
        }
    
        func didItemUpdated() {
            // Handle item updated event
        }
    }
    
    
    class TaskViewModel {
        static let shared = TaskViewModel()
        var tasks = [Task]()
        
        private init() {
            // Clear the existing tasks array
            tasks.removeAll()
            // Get all records from CoreData
            fetchAllTasks()
        }
    
        var numberOfTasks: Int {
            tasks.count
        }
    
        func fetchAllTasks() {
            // Fetch all data from core data
            tasks = CoreDataManager.shared.fetchAll().map(Task.init)
            print("Number of tasks after fetching: \(tasks.count)")
        }
    
        func numberOfRows(by section:Int) -> Int {
            if section == 0 {
                return 1
            }
            return numberOfTasks
        }
    
        func getTasksByType() -> (complete: Int, InComplete: Int) {
            let completedCount = tasks.lazy.filter({ $0.completed }).count
            let InCompletedCount = tasks.lazy.filter({ !$0.completed }).count
            
            return (completedCount,InCompletedCount)
        }
    
        func task(by index: Int) -> Task {
            return tasks[index]
        }
    
        func addNewTask(name: String) {
            let newItem = Item(context: CoreDataManager.shared.context)
            newItem.id = UUID()
            newItem.completed = false
    
            newItem.name = name
            newItem.createdAt = Date.now
    
            let newTask = Task(task: newItem)
            tasks.append(newTask)
    
            CoreDataManager.shared.addNewItem(item: newItem)
    
        }
    
        func toggleCompleted(task: Task) {
            CoreDataManager.shared.toggleCompleted(id: task.id)
            // call core data to toggle
            fetchAllTasks()
        }
    
        func deleteItem(task: Task) {
            CoreDataManager.shared.deleteItem(id: task.id)
            // call core data to delete the task
            fetchAllTasks()
        }
    
    }
    

    任务视图模型.swift

    class TaskViewModel {
        static let shared = TaskViewModel()
        var tasks = [Task]()
        
        private init() {
            // Clear the existing tasks array
            tasks.removeAll()
            // Get all records from CoreData
            fetchAllTasks()
        }
    
        var numberOfTasks: Int {
            tasks.count
        }
    
        func fetchAllTasks() {
            // Fetch all data from core data
            tasks = CoreDataManager.shared.fetchAll().map(Task.init)
            print("Number of tasks after fetching: \(tasks.count)")
        }
    
        func numberOfRows(by section:Int) -> Int {
            if section == 0 {
                return 1
            }
            return numberOfTasks
        }
    
        func getTasksByType() -> (complete: Int, InComplete: Int) {
            let completedCount = tasks.lazy.filter({ $0.completed }).count
            let InCompletedCount = tasks.lazy.filter({ !$0.completed }).count
            
            return (completedCount,InCompletedCount)
        }
    
        func task(by index: Int) -> Task {
            return tasks[index]
        }
    
        func addNewTask(name: String) {
            let newItem = Item(context: CoreDataManager.shared.context)
            newItem.id = UUID()
            newItem.completed = false
    
            newItem.name = name
            newItem.createdAt = Date.now
    
            let newTask = Task(task: newItem)
            tasks.append(newTask)
    
            CoreDataManager.shared.addNewItem(item: newItem)
    
        }
    
        func toggleCompleted(task: Task) {
            CoreDataManager.shared.toggleCompleted(id: task.id)
            // call core data to toggle
            fetchAllTasks()
        }
    
        func deleteItem(task: Task) {
            CoreDataManager.shared.deleteItem(id: task.id)
            // call core data to delete the task
            fetchAllTasks()
        }
    
    }
    

    核心数据管理器.swift

    class CoreDataManager {
        static let shared = CoreDataManager()
    
        private init() {}
    
        // persistence CoreDataModel
        lazy var persistentContainer: NSPersistentContainer = {
            // Name of the CoreDataModel -- Items
            let container = NSPersistentContainer(name: "Items")
            container.loadPersistentStores { _ , error in
                if let error = error as NSError? {
                    fatalError("Unresolved error \(error), \(error.userInfo)")
                }
            }
            return container
        }()
        
        // for saving -- save, delete etc.
        var context: NSManagedObjectContext {
            persistentContainer.viewContext
        }
    
        func saveContext() {
            if context.hasChanges {
                do {
                    try context.save()
                } catch {
                    let nserror = error as NSError
                    print("Error - saveContext:", nserror.description, nserror.userInfo)
                }
            }
        }
    
        // Fetch all Items from CoreData
        func fetchAll() -> [Item] {
            var tasks = [Item]()
            
            // EntityName.fetchRequest()
            let fetchRequest: NSFetchRequest<Item> = Item.fetchRequest()
            let sortByCreatedDate = NSSortDescriptor(key: "createdAt", ascending: true)
    
            fetchRequest.sortDescriptors = [sortByCreatedDate]
    
            do {
                tasks = try context.fetch(fetchRequest)
            } catch {
                let nserror = error as NSError
                print("Error - fetchAll:", nserror.description, nserror.userInfo)
            }
    
            return tasks
        }
    
        // Add a item to CoreData
        func addNewItem(item: Item) {
            // create a new item if this item doesn't exist
    
            // save changes
            saveContext()
        }
        
        // Toggle Completed from CoreData
        func toggleCompleted(id: UUID) {
            let fetchRequest: NSFetchRequest<Item> = Item.fetchRequest()
            let predicate = NSPredicate(format: "id=%@", id.uuidString)
            fetchRequest.predicate = predicate
    
            do {
                if let fetchedTasks = try context.fetch(fetchRequest).first(where: { foundItem in
                    foundItem.id == id
                }) {
                    fetchedTasks.completed = !fetchedTasks.completed
                }
    
                // Save Core Data
                saveContext()
    
            } catch let error as NSError {
                print("toggleCompleted Error: \(error) \(error.userInfo)")
            }
        }
    
        // Delete item from CoreData
        func deleteItem(id: UUID) {
            let fetchRequest: NSFetchRequest<Item> = Item.fetchRequest()
    
            let predicate = NSPredicate(format: "id=%@", id.uuidString)
            fetchRequest.predicate = predicate
    
            do {
                let fetchedTasks = try context.fetch(fetchRequest)
    
                for task in fetchedTasks {
                    context.delete(task)
                }
    
                // Save Core Data
                saveContext()
    
            } catch let error as NSError {
                print("deleteItem Error: \(error) \(error.userInfo)")
            }
        }
    
    }
    

    添加新任务视图Controller.swift

    class AddNewTaskViewController: UIViewController {
        weak var delegate: ItemControllerDelegate?
    
        lazy var taskNameLabel: UILabel = {
            let label = UILabel()
            label.translatesAutoresizingMaskIntoConstraints = false
            label.text = "Task Name"
            return label
        }()
    
        lazy var taskNameTextField: UITextField = {
            let textField = UITextField()
            textField.translatesAutoresizingMaskIntoConstraints = false
            textField.placeholder = "Enter Task Name"
            textField.borderStyle = .roundedRect
            return textField
        }()
    
        let viewModel = TaskViewModel.shared
    
        override func viewDidLoad() {
            super.viewDidLoad()
            
            setupView()
        }
    
        private func setupView() {
            view.backgroundColor = .systemBackground
            title = "Add New Task"
            navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Save", style: .done, target: self, action: #selector(savePressed))
    
            view.addSubview(taskNameLabel)
            view.addSubview(taskNameTextField)
            setupConstraints()
        }
    
        private func setupConstraints() {
            NSLayoutConstraint.activate([
                taskNameLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 15),
                taskNameLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 15),
                taskNameLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -15),
    
                taskNameTextField.topAnchor.constraint(equalTo: taskNameLabel.bottomAnchor, constant: 15),
                taskNameTextField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 15),
                taskNameTextField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -15),
            ])
        }
    
        // Action Function
        @objc func savePressed() {
            guard let name = taskNameTextField.text else { 
                let alert = UIAlertController(title: "Error", message: "Name can't be empty", preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "OK", style: .default))
                present(alert, animated: true)
                return }
    
            viewModel.addNewTask(name: name)
    
            delegate?.didItemAdded()
    
            navigationController?.dismiss(animated: true)
        }
    }
    

    我曾尝试在从Core Data获取任务之前清除任务数组,但问题仍然存在。我做错了什么?如何防止表视图中的任务重复?

    如有任何帮助或见解,我们将不胜感激!提前谢谢。

    1 回复  |  直到 1 年前
        1
  •  0
  •   Joakim Danielson    1 年前

    在委托方法中 numberOfSections(in:) 您返回的总行数肯定是错误的

    func numberOfSections(in tableView: UITableView) -> Int {
        return viewModel.numberOfTasks
    }
    

    查看函数 numberOfRows(by:) 在您的视图模型中,我假设节的数量是两个,所以将委托方法更改为

    func numberOfSections(in tableView: UITableView) -> Int {
        return 2
    }