使用 Swift 在 tableview 中使用 headers 部分进行复制

Posted

技术标签:

【中文标题】使用 Swift 在 tableview 中使用 headers 部分进行复制【英文标题】:duplication using headers section in tableview using Swift 【发布时间】:2020-08-03 15:27:45 【问题描述】:

好吧,每次用户完成任务时,我都会尝试将日期添加到我的标题部分。问题是标题部分有点新问题,由于某种原因,每次我在“完成”部分中添加两个以上的任务时,它似乎都会自我复制。

查看了 *** 并找不到我需要的东西,我希望你们能帮助我:) 这是有问题的应用程序的图片:https://imgur.com/wjVy9Uy

这是我的代码:

import Foundation
import CoreData
import UIKit
import SwipeCellKit

protocol TaskDelegate 
    func updateTaskName(name:String)


class TasksManViewController: UITableViewController, SwipeTableViewCellDelegate 
    
    
    
    @IBOutlet weak var Sege: UISegmentedControl!
    
    
    
    var tasksArray = [Task]()
        didSet 
            // because we perform this operation on the main thread, it is safe
            DispatchQueue.main.async 
                self.tableView.reloadData()
            
        
    
    
    
    let isSwipeRightEnabled = true

    var delegate: TaskDelegate?

    let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    
    
    override func viewDidLoad() 
        
    
    
    
    override func viewWillAppear(_ animated: Bool) 
        loadTasks()
    

    
    
    // MARK: - DataSource + Delegate Methods:
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return tasksArray.count
    
    
    
    override func numberOfSections(in tableView: UITableView) -> Int 
        return tasksArray.count
    
    
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCell(withIdentifier: "taskCellRow") as! SwipeTableViewCell
        cell.delegate = self
        
        cell.textLabel?.text = tasksArray[indexPath.row].title
        
        return cell
    
    
    
    
    func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]? 
        if orientation == .left 
            
            guard isSwipeRightEnabled else  return nil 

            let doneAction = SwipeAction(style: .destructive, title: "Done")  (action, indexPath) in
                
                //STEP1: Append the task to the doneTasksArr:
                self.tasksArray[indexPath.row].isDone = true
                
                //STEP3: Remove the Row:
                self.tasksArray.remove(at: indexPath.row)
                
                //STEP4: Update the Model:
                self.saveTasks()
                
                self.delegate?.updateTaskName(name: "")
                
                self.tableView.reloadData()
                
            
            
            let unDoneAction = SwipeAction(style: .destructive, title: "Undone")  (unDoneAction, indexPath) in
                self.tasksArray[indexPath.row].isDone = false
                
                self.tasksArray.remove(at: indexPath.row)
                
                self.saveTasks()
                
            
            
            
            //configure btn:
            doneAction.backgroundColor = .cyan
            unDoneAction.backgroundColor = .blue
            
            
            if Sege.selectedSegmentIndex == 0  //Doing this to allow the user to unDone tasks.
            return [doneAction]
             else  return [unDoneAction] 

            
         else 
            let deleteAction = SwipeAction(style: .destructive, title: "Delete")  action, indexPath in
                
                self.context.delete(self.tasksArray[indexPath.row])
                
                self.tasksArray.remove(at: indexPath.row)
                
                
                self.saveTasks()
                
                self.delegate?.updateTaskName(name: "")

                self.tableView.reloadData()
                
            
            
            return [deleteAction]
        
        
    
    
    
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 
        tableView.deselectRow(at: indexPath, animated: true)
        
        
    
    
    // MARK: - Class Methods:
    @IBAction func addBtnTapped(_ sender: UIBarButtonItem) 
        Sege.selectedSegmentIndex = 0 // Doing this to avoid the user to insert a task into the DoneTasks by mistake! and avoiad a bug :)
        insertNewTask()
    
    
    
    
    func insertNewTask() 
        var textField = UITextField()
        
        
        let alert = UIAlertController(title: "New Task", message: "Please Add Your Task", preferredStyle: .alert)
        
        alert.addTextField  (alertTextField) in
            alertTextField.placeholder = "Create New Task"
            textField = alertTextField
        
        
        
        let action = UIAlertAction(title: "Add", style: .default)  (action) in
            
            let newItem = Task(context: self.context)
            
            newItem.title = textField.text!
            newItem.isDone = false
            newItem.date = Date()
            
            
            self.tasksArray.append(newItem)

            self.delegate?.updateTaskName(name: newItem.title!)
            
            self.saveTasks()
            
        
        
        
        
        alert.addAction(action)
        
        
        
        self.present(alert, animated: true, completion: nil)
        
    
    
    
    // MARK: - Sege Section:
    @IBAction func segeControlTapped(_ sender: UISegmentedControl) 
        
        switch Sege.selectedSegmentIndex
        
        case 0:
            //Loading normal tasks whose are not done
            loadTasks()
            
        case 1:
            //Loading the doneTasks:
            loadDoneTasksFrom()

            
            
        default:
            print("There's something wrong with Sege!")
        
        
    
    
    
    
    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? 
        let completedTasksToDisply = 0
        
        if Sege.selectedSegmentIndex == 1 
            if let firstTask = tasksArray[section].date 
            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = "dd/MM/yyyy"
            let dateString = dateFormatter.string(from: firstTask)
            
            return   " " + dateString + " " +  " " + "Completed Tasks: \(completedTasksToDisply)"
            
            
        
        
        
        return ""

    
    
    override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) 
        let header: UITableViewHeaderFooterView = view as! UITableViewHeaderFooterView
        header.textLabel?.font = UIFont(name: "Verdana", size: 13.0)
        header.textLabel?.textAlignment = NSTextAlignment.center
        
    
    
    
    
    
    //MARK: - Model Manipulation Methods:
    func saveTasks() 
        do 
            try! context.save()
         catch let err 
            print("Error Saving context \(err)")
        
    
    
    
    func loadTasks() 
        let request: NSFetchRequest<Task> = Task.fetchRequest()
        
        request.predicate = NSPredicate(format: "isDone == %@", NSNumber(value: false))
        
        request.sortDescriptors = [NSSortDescriptor(key: "isDone", ascending: false)]
        
        do
            tasksArray = try! context.fetch(request)
         catch 
            print("There was an error with loading items \(error)")
        
        
        tableView.reloadData()

    
    
    
    func loadDoneTasksFrom() 
        let request:NSFetchRequest<Task> = Task.fetchRequest()

        request.predicate = NSPredicate(format: "isDone == %@", NSNumber(value: true))
        
        request.sortDescriptors = [NSSortDescriptor(key: "isDone", ascending: false)]
        
        do
            tasksArray = try context.fetch(request)
         catch 
            print("Error fetching data from context\(error)")
        
        
        tableView.reloadData()
        
    
    

【问题讨论】:

tableView(_ tableView:titleForHeaderInSection:) 中你不应该测试你在哪个部分而不是Sege 您有一个一维数据源数组,您将返回 numberOfRowsInSectionnumberOfSections 中的项目数。这行不通。对于部分,您需要一个二维数组或 - 最好 - 一个 Section 结构。并且数据源数组的didSet 属性观察器不是一个好习惯。 @vadian 哦 :\ 我的错,会解决这个问题。你我会尝试更多地了解这一点。 【参考方案1】:

编辑:好的,我知道您要做什么。试一试——我们需要一个双精度数组——里面的每个数组都必须按日期分割——我们的查询看起来像这样:

var tasksArray = [[Task]]()
var sectionDates = [String]()

func loadTasks() 
    
    tasksArray.removeAll(keepingCapacity: false)
    sectionDates.removeAll(keepingCapacity: false)
    
    let request: NSFetchRequest<Task> = Task.fetchRequest()
        
    request.predicate = NSPredicate(format: "isDone == %@", NSNumber(value: false))
        
    request.sortDescriptors = [NSSortDescriptor(key: "isDone", ascending: false)]
        
    do
        let tasks = try! context.fetch(request)

        for task in tasks 
             if let taskDate = task.date 
                
                 let dateFormatter = DateFormatter()
                 dateFormatter.dateFormat = "dd/MM/yyyy"
                 let dateString = dateFormatter.string(from: taskDate)
                 
                 if self.sectionDates.contains(dateString) 

                    if let section = self.sectionDates.firstIndex(of: dateString) 
                        self.tasksArray[section].append(task)
                    
                    
                  else 
                    self.sectionDates.append(dateString)

                    // EDIT - ADD THIS LINE (and self. where necessary)
                    self.tasksArray.append([])

                    if let section = self.sectionDates.firstIndex(of: dateString) 
                        self.tasksArray[section].append(task)
                    
                 
             
        

     catch 
        print("There was an error with loading items \(error)")
    
        
    tableView.reloadData()


按顺序加载对象也可能会有所帮助,但通过此设置,无论顺序如何,它仍应正确分组所有内容 - 除非您希望您的部分按特定顺序排列,否则您可以在查询中指定

现在更改您的 tableView 委托方法

override func numberOfSections(in tableView: UITableView) -> Int 
    return sectionDates.count


override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
    return tasksArray[section].count


override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
    let cell = tableView.dequeueReusableCell(withIdentifier: "taskCellRow") as! SwipeTableViewCell
    cell.delegate = self
    
    cell.textLabel?.text = tasksArray[indexPath.section][indexPath.row].title
    
    return cell


override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? 
    let completedTasksToDisply = 0
        
    if Sege.selectedSegmentIndex == 1 
        let dateString = sectionDates[section]
        return   " " + dateString + " " +  " " + "Completed Tasks: \(completedTasksToDisply)"
    
    return ""

我想我会将其余的 tableView 委托方法留给您,但在大多数情况下,我们只需要在使用双精度数组时识别 indexPath 行之前的部分 - 就像我们在 cellForRow 中所做的那样

我运行了这段代码,结果如下:

【讨论】:

@robots 首先非常感谢您的回答,其次您有任何提示可以引导我做我愿意做的事情吗? :) 我有点卡住了,因为当我不需要它时,我很难做一个二维数组:\ 我不确定我是否理解这个问题。你想完成什么? @robots 基本上我想要完成的是能够根据已完成的任务将日期插入标题中,例如,如果我今天已经完成了 3 个任务,那么它将在日期标题下组合在一起。 好的,我现在明白了。检查更新的答案。它应该可以帮助您朝着正确的方向前进 对不起,我正在度假——我运行了代码,但遗漏了一行:taskArray.append([])——我会更新我的答案

以上是关于使用 Swift 在 tableview 中使用 headers 部分进行复制的主要内容,如果未能解决你的问题,请参考以下文章

Swift-使用委托在 tableView 上显示

在 tableView Swift 中使用 javascript 数据

Swift - 在 TableView 单元中使用步进器增加标签

在 Swift 中使用远程 JSON 数据填充 tableView

在swift中使用tableView自动填充代理方法的最佳方法?

使用Swift查找用户是不是已经显示在TableView中[重复]