我正在开发一个使用 Core Data 和 UITableView 来管理任务的 iOS 应用程序。当我添加新任务时,UITableView 显示现有任务的重复条目。
这是管理任务和更新表视图的代码:
这是添加新任务后问题的 gif:问题的 gif
ViewController.Swift
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()
}
}
TaskViewModel.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()
}
}
CoreDataManager.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)")
}
}
}
添加NewTaskViewController.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)
}
}
什么可能导致 UITableView 中的任务重复,如何解决此问题?
在委托方法中
numberOfSections(in:)
你返回的总行数一定是错误的
func numberOfSections(in tableView: UITableView) -> Int {
return viewModel.numberOfTasks
}
查看视图模型中的函数
numberOfRows(by:)
,我假设部分的数量是两个,因此将委托方法更改为
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}