Tutorial 2: Checklists (V5) - TableView "static" cells vs. TableViewHeader construct

I’m going through this tutorial a second time to increase my understanding by trying some variations on core concepts. More fully understanding TableViews is obviously a core competency one needs as an iOS developer.

This got me to wondering about “header” content in TableViews. Although we don’t really need static “header” information in this example app, things we list in a TableView often will, and how to do this is way less obvious to me than I would have hoped.

I did some Googling, and I found two solutions: the tableHeaderView “accessory view” and adding an additional Prototype Cell to create a new TableView Section (I think), then add logic to my “override func tableview ( … cellForRowAt …)” method.

That “tableHeaderView” sounds perfect, but Apple’s API Reference documentation consists of this:

The default value is nil. The table header view is different from a section header.

That’s not so helpful, so I tried to adapt some StackOverflow answers for creating/populating a static Prototype Cell.

In my list of checklists (the TableView) in CheckListViewController, I increased the Prototype Cells value to 2:

I added a label to that new prototype cell, then added a method to return the number of sections:

override func numberOfSections(in tableView: UITableView) -> Int {
    return 2 // First section: Static; Second section dynamic
}

I also modified the tableView(numberOfRowsInSection ) method to deal with the additional section:

    override func tableView(_ tableView: UITableView,
                        numberOfRowsInSection section: Int) -> Int {
    if (section == 1){ // i.e., the dynamic section:
        return items.count
    }
    else { // the static section
        return 1
    }
}

I then modified my tableview( … cellForRowAt …) method to populate the different sections differently:

    override func tableView(_ tableView: UITableView,
                        cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    let item = items[indexPath.row]
    
    // Static "header" section:
    if (indexPath.section == 0) {
        let cell = self.tableView.dequeueReusableCell(
            withIdentifier: "ItemHeader", for: indexPath)
     
        configureHeaderRow(for: cell)
        
    } else { // Dynamic content:
    
        let cell = tableView.dequeueReusableCell(
            withIdentifier: "ChecklistItem", for: indexPath)
    
        configureText(for: cell, with: item)
    }
    return cell
}

You are probably already seeing the problem, but my beginner-mind can’t see it. I’m getting this error:

I’m guessing that this is a scoping error, but I’m not seeing where it comes from.

Help in understanding this Rookie Error would be awesome. As an added bonus, advice for how to best implement headers would be even better.

Thanks!

1 Like

It’s a scoping error. You’ve declared the variable with ‘let cell’ inside the scope, so when the scope ends, cell disappears. There’s no cell outside of the scope to return.

If you write let cell : UITableViewCell immediately before the ‘if’, without initialising it, and remove the let from inside each of the two scopes, Swift’s smart enough to analyse the flow of your program, realise that both the ‘if true’ and ‘if false’ paths will initialise cell, and let you return cell with the certainty that it has a value.
But, just as simply, you could write ‘return cell’ inside both scopes, too.

1 Like

YES! It was as simple as that. Updating to this did the trick:

override func tableView(_ tableView: UITableView,
                    cellForRowAt indexPath: IndexPath) -> UITableViewCell {

let item = items[indexPath.row]
let cell: UITableViewCell                //*** ADDED ***

// Static "header" section:
if (indexPath.section == 0) {
       cell = self.tableView.dequeueReusableCell(          // *** Drop the "let" ***
         withIdentifier: "ItemHeader", for: indexPath)
 
       configureHeaderRow(for: cell)
    
   } else { // Dynamic content:

      cell = tableView.dequeueReusableCell(               // *** Drop the "let" ***
        withIdentifier: "ChecklistItem", for: indexPath)

      configureText(for: cell, with: item)
    }
   return cell
}

Many thanks!

Getting my “static Prototype cell” hack working (as we just did) is great, but it really doesn’t solve my “Proper Header” problem.

  • My “Hack Header” scrolls up out of view just like any other table cell.

I remain open to advice about how to do TableView headers properly :sunglasses:

Inside the storyboard, you can simply drag a UIView (or any other view) into the table view where the header (or footer) goes.

Ummmmm … WAY too easy! (Done!) :cake:

Many thanks!