Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sectioning UITableView cells

I'm trying to organize my tableview cells into sections from an element of the items in the list (dueTime). Firebase is my backend and each item has a child node called dueTime with a string of the time. I have created the sections and have gotten them to show up but I need the actual items to separate into them. Currently, when I run my code, all that shows up are the sections.

    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]
    }

I feel like the source of the problem is probably in numberOfRowsInSection and in cellForRowAtIndexPath. From numberOfRowsInSection, when I print self.sectionTasks after I set it equal to newTasks I get 6 arrays (equal to the number of sections) with all of the correct tasks in their correct arrays. But, when I print self.sectionTasks.countI get '0' six times which doesn't make sense to me. I have no idea what to do in cellForRowAtIndexPath. I can't seem to find a good tutorial anywhere that explains it well.

Update 1:

I've also tried this in 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
    }

I get the same outcome. Basically, this way gives me just the titles of the items instead of the entire item in each array. But it still tells me that the count is 0 for all of the arrays.

Update 2:

After implementing the following code, I am now getting all of the tasks duplicated under each section. I would imagine I need to somehow filter the tasks but I am not sure where or how to do that.

 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()
        })
    }

I am running this function in viewDidLoad() and am getting one big array with all of the elements of each task inside. I would imagine I need to use the " "section" element from numberOfRows but I am not sure how. Also, the tutorials I am looking at show the use of indexPath.section in cellForRow but I am just not sure how to implement either of those things.

Solution:

I ended up changing the querying code altogether so I pulled from a local array instead of 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]
    }
like image 899
Michael Williams Avatar asked Dec 17 '25 11:12

Michael Williams


1 Answers

This happens because call to taskRef.query... is asynchronous. And numberOfRowsInSection method expects at that point that you already know number of rows that you have.

Move querying code into some other method, e.g.

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()
            })
}

Then, call queryDueTimes method from the viewDidLoad() method of your view controller:

func viewDidLoad()
{
    super.viewDidLoad()

    var uniqueSectionTimes = Array(Set(sectionTimes))
    self.queryDueTimes(uniqueSectionTimes)
}
like image 51
Eugene Dudnyk Avatar answered Dec 19 '25 23:12

Eugene Dudnyk



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!