UITableView单元格分组

3

我正在尝试从列表中的一个元素(dueTime)将我的表格视图单元格组织成部分。Firebase是我的后端,每个项目都有一个名为dueTime的子节点,其值为时间字符串。我已经创建了这些部分并使它们显示出来,但我需要实际的项目分开放置到它们所属的部分中。目前,当我运行我的代码时,只有这些部分显示出来。

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        let tasksRef = ref.childByAppendingPath("tasks")
        tasksRef.observeSingleEventOfType(.Value, withBlock: {snapshot in
            var dueTimesArray = [String]()
            for task in snapshot.children.allObjects as! [FDataSnapshot] {
                let times = task.value["dueTime"] as! String
                dueTimesArray.append(times)
            }
            self.sectionTimes = dueTimesArray
        })
        let uniqueSectionTimes = Array(Set(sectionTimes))
        return uniqueSectionTimes.count
    }


    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        var uniqueSectionTimes = Array(Set(sectionTimes))
        let tasksRef = Firebase(url: "\(self.ref)/tasks")
        tasksRef.queryOrderedByChild("dueTime").queryEqualToValue(uniqueSectionTimes[section]).observeEventType(.Value, withBlock: { snapshot in
            var newTasks = [Task]()
            for task in snapshot.children.allObjects as! [FDataSnapshot] {
                let tasks = Task(snapshot: task)
                newTasks.append(tasks)
            }
            self.sectionTasks = newTasks
        })
        return self.sectionTasks.count
    }


    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("TaskCell", forIndexPath: indexPath) as! TaskCell

        // Configure the cell...
        cell.selectionStyle = .None
//        let uniqueSectionTimes = Array(Set(sectionTimes))
//        let times = self.sectionTasks[uniqueSectionTimes[indexPath.section]]
        let task = tasks[indexPath.row]
        cell.label.text = task.title
        ref.childByAppendingPath("tasks").observeEventType(.Value, withBlock: { snapshot in
        if task.done == true {
            cell.checkBox.image = UIImage(named: "checkedbox")
            cell.detailLabel.text = "Completed By: \(task.completedBy)"
            }
            else {
            cell.checkBox.image = UIImage(named: "uncheckedbox")
            cell.detailLabel.text = ""
            }
        })

        cell.delegate = self
        return cell
    }

    override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        let uniqueSectionTimes = Array(Set(sectionTimes))

        return uniqueSectionTimes[section]
    }

我觉得问题的根源可能在于numberOfRowsInSection和cellForRowAtIndexPath方法。在numberOfRowsInSection中,当我将self.sectionTasks设置为newTasks后,打印self.sectionTasks会输出6个数组(等于section的数量),每个数组中都有正确的任务。但是,当我打印self.sectionTasks.count时,我六次都得到了'0',这对我来说没有意义。在cellForRowAtIndexPath中,我不知道该怎么做。我似乎找不到一个好的教程来解释它。
更新1:
我也在numberOfRows中尝试过这个方法。
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        var uniqueSectionTimes = Array(Set(sectionTimes))
        let tasksRef = Firebase(url: "\(self.ref)/tasks")
        tasksRef.queryOrderedByChild("dueTime").queryEqualToValue(uniqueSectionTimes[section]).observeEventType(.Value, withBlock: { snapshot in
            var newTasks = [String]()
            for task in snapshot.children.allObjects as! [FDataSnapshot] {
                let tasks = task.value["title"] as! String
                newTasks.append(tasks)
            }
//            for task in snapshot.children.allObjects as! [FDataSnapshot] {
//                let tasks = Task(snapshot: task)
//                newTasks.append(tasks)
//            }
            self.sectionTasks = newTasks
        })
        print(sectionTasks.count)
        return self.sectionTasks.count
    }

我得到了相同的结果。基本上,这种方法只给我每个数组中项目的标题,而不是整个项目。但它仍然告诉我所有数组的计数为0。
更新2:
在实现以下代码后,我现在在每个部分下都会获得所有任务的副本。我想我需要以某种方式过滤任务,但我不知道在哪里或如何做到这一点。
 func queryDueTimes(uniqueSectionTimes:Array<String>) {
        let tasksRef = Firebase(url: "\(self.ref)/tasks")
        tasksRef.queryOrderedByChild("dueTime").observeEventType(.Value, withBlock: { snapshot in
            var newTasks = [Task]()
            for task in snapshot.children.allObjects as! [FDataSnapshot] {
                let times = Task(snapshot: task)
                newTasks.append(times)
            }
            self.sectionTasks = newTasks
            print(self.sectionTasks)
            self.tableView.reloadData()
        })
    }

我在viewDidLoad()中运行此函数,得到了一个包含每个任务元素的大数组。我想我需要使用numberOfRows中的“section”元素,但我不确定如何操作。此外,我查看的教程展示了在cellForRow中使用indexPath.section,但我不确定如何实现这两个操作。 解决方案: 最终,我彻底改变了查询代码,因此从本地数组中提取而不是从Firebase中提取。
func querySections() -> [String] {
           var sectionsArray = [String]()
        for task in tasks {
            let dueTimes = task.dueTime
            sectionsArray.append(dueTimes)
        }
        let uniqueSectionsArray = Array(Set(sectionsArray)).sort()
        return uniqueSectionsArray
    }


    func queryDueTimes(section:Int) -> [Task] {
        var sectionItems = [Task]()
        for task in tasks {
            let dueTimes = task.dueTime
            if dueTimes == querySections()[section] {
                sectionItems.append(task)
            }
        }
        return sectionItems
    }



    // MARK: - Table view data source

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return querySections().count
    }



    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return queryDueTimes(section).count
    }


    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("TaskCell", forIndexPath: indexPath) as! TaskCell

        // Configure the cell...
        cell.selectionStyle = .None
        let times = queryDueTimes(indexPath.section)
        let task = times[indexPath.row]
        cell.label.text = task.title
        if task.done == true {
            cell.checkBox.image = UIImage(named: "checkedbox")
            cell.detailLabel.text = "Completed By: \(task.completedBy)"
            }
            else {
            cell.checkBox.image = UIImage(named: "uncheckedbox")
            cell.detailLabel.text = ""
            }

        cell.delegate = self
        return cell
    }

    override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return querySections()[section]
    }

当我看到接受块作为参数的调用时,我怀疑它们可能是异步的。observeEventType函数是否也是如此?如果是的话,在任务填充之前你可能会返回一个计数。 (尝试在块内部再添加一个打印语句来确认事件的顺序。) - Phillip Mills
有很多UITableView教程可用,因此我首先建議在引入Firebase方面之前,先熟悉創建和填充靜態數據的tableView。cellForRowAtIndexPath允許表格重複使用已經創建的單元格,從而避免為每個所需的單元格分配新的單元格等問題。大局是您需要先從Firebase分別讀取您的數據,然後才能嘗試填充表格。此外,如果您正在將觀察者添加到Firebase中,每次發生事件時,您都需要清除數組和表格並重新填充它。 - Jay
我很熟悉填充tableView。我理解cellForRow的作用以及如何使用它。我的问题是如何结合使用sections。 - Michael Williams
1个回答

2
这是因为调用taskRef.query...是异步的,而numberOfRowsInSection方法在此时期望您已经知道了行数。
将查询代码移到其他方法中,例如:
func queryDueTimes(uniqueSectionTimes: Array) { 
    let tasksRef = Firebase(url: "\(self.ref)/tasks")
            tasksRef.queryOrderedByChild("dueTime").queryEqualToValue(uniqueSectionTimes[section]).observeEventType(.Value, withBlock: { snapshot in
                var newTasks = [String]()
                for task in snapshot.children.allObjects as! [FDataSnapshot] {
                    let tasks = task.value["title"] as! String
                    newTasks.append(tasks)
                }
    //            for task in snapshot.children.allObjects as! [FDataSnapshot] {
    //                let tasks = Task(snapshot: task)
    //                newTasks.append(tasks)
    //            }
                self.sectionTasks = newTasks


               //IMPORTANT: reload your table here:
                self.tableView.reloadData()
            })
}

然后,在您的视图控制器的viewDidLoad()方法中调用queryDueTimes方法:

func viewDidLoad()
{
    super.viewDidLoad()

    var uniqueSectionTimes = Array(Set(sectionTimes))
    self.queryDueTimes(uniqueSectionTimes)
}

我遇到了关于queryDueTimes函数第一行的错误,所以我将其更改为(uniqueSectionTimes:Array<String>),但现在在queryEqualToValue中无法访问"section"。你有什么想法吗? - Michael Williams
你应该请求完整的数据集,而不是按部分请求。或者,你可以在这里请求所有你拥有的部分,并将它们按部分存储起来,当你收到所有回应时再重新加载表格。 - Eugene Dudnyk
我更新了帖子,包括我的最新问题。如何使用section元素将任务实际过滤到不同的section数组中,并且如何在cellForRow中使用indexPath.section? - Michael Williams
为了确保你理解我的意思:有两种可能性 - 一种是获取完整的数据集并手动按部分拆分(如果你知道如何操作)。第二种方法是按部分请求数据集。看起来你实现了第一种方法,但不知道如何按部分拆分。我认为没有人熟悉你的数据,也没有人比你更了解它。 - Eugene Dudnyk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接