How to design a table header in IB

I would add a header, UITableViewHeaderFooterView, to a table, UITableView.

I would design it in IB and then use dequeueReusableHeaderFooterView, but any attempt failed for various reasons.

Have I to add a UIView in the main Storyboard, o prepare a new dedicated storyboard, or a xib file?

How to connect it to the swift code?


Thanks

Replies

Here how I did this.


I needed a headerCell and content cells. In the same section, I will have several HeaderCells, each followed by several ContentCells. I did not use UITableViewHeaderFooterView.


I declared 2 prototype cells in TableView (IB)

The first is a plain UITableViewCell, with identifier ContentCell.

Inside there is an image, Title and subtitle, disclosure icon.


I created a subClass HeaderCell of UITableViewCell, with its xib (named as HeaderCell.xib).

In the ViewController:

- register the nib

        let nib = UINib(nibName: "HeaderCell", bundle: nil)
        tableView.register(nib, forCellReuseIdentifier: "HeaderCell")


In func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)

I use HederCell or ContentCell as needed.

hi,


this is also one way to do it that avoids creating extra .xib files


(1) drag a custom view onto the TableView _above_ the prototype cells (or better, into the struture structure view so you can be sure of the placement). this will become a header view, which you can then populate with content as you wish.


(2) drag a custom view onto the TableView _below_ the protype cells. this will become a footer view ...


(3) check that you've placed those views correctly in the scene. for example, if you your header and footer each have a simple label such as "Header" and "Footer", you'll see the structure as


TableView

---- View

-------- [L] Header

---- Cell (EDIT: assumes just one prototype cell)

-------- Content View

------------ Title (if it's a Basic cell)

---- View

-------- [L] Footer


if you have more elaborate design issues for your header and footer, however, Claude's suggestion might be preferred for organization.


hope that helps,

DMG

Thank you Claude31 and DelawareMathGuy,

the header cell works, but it isn't a UITableViewHeaderFooterView recalled with dequeueReusableHeaderFooterView as suggested by Apple.

I tried with a xib file associated with UITableViewHeaderFooterView, but it is displayed empty.

hi,


my apologies for any confusion to both of you on this, since i was answering thinking that you were asking about the header/footer of the entire table, not headers for individual sections. what i described was to add a header or footer to the table (both UITableView.tableHeaderView and UITableView.tableFooterView are of type UIView?) and there would be no need to make those reusable.


for _section_ headers and footers, you probably might want to go the route of recycling headers and footers -- although if you have a small number of sections and not much data, i doubt that it would matter.


so, best i can offer would be these things:


(1) there's this recent article on NSHipster about section headers and .xib files that might have more information related to Claude31's suggestion.


(2) Mark Moeykens's Itinerary Series on YouTube has one segment ("Custom TableView Section Headers - Part 25 - Itinerary App (iOS, Xcode 10, Swift 4") devoted to (recyclable) section headers using a combination of (the contentView of) a custom prototype cell in the storyboard and using the UITableView.viewForHeaderInSection method.


hoe that helps,

DMG

Thank you @DelawareMathGuy for your explanations and references.
I'm still in trouble; I noticed that the xib file identity inspector don't list the UITableViewHeaderFooterView or my class subclassed from it. I can write its name, but then the tableView.dequeueReusableHeaderFooterView fails (return nil).

Perhaps the issue is connected to the fail in listing the UITableViewHeaderFooterView and its subclasses.

NSHipster's article don't exemplify this aspect and others suggest to use UITableViewCell, but UITableViewHeaderFooterView is a view, not a cell.

hi Luca_65,


so, full disclosure: i am using some custom section headers, but simply overriding tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) to create the view in code. it's a simple enough header - i really just want a background color, a centered label, and not much more.


i have usually stayed away from nib files for custom views and things.


but, after i mentioned the NSHipster article, i tried to follow its blueprint and ran into some frustration, which sounds about the same as what you are experiencing. and, much as I love the Moeykens videos, i use the same style heared shared across multiple tables, and i don't want to reinvent the header using a UITableViewCell in each table.


after seeing your recent post, i felt is should go back and try again, and after a little extra frustration, finally got something that works. (it's a hybrid of some of what you've seen in this discussion):


(1) create a UITableViewCell class to define your header, e.g., TableViewSectionHeaderCell.swift (yes, create a subclass of UITableViewCell, not a subclass of the TableViewHeaderFooterView class or the UIView class)


(2) also add it in with its own nib, e.g., TableViewSectionHeaderCell.xib. (your .xib should have just a custom UITableViewCell dragged out into it, which is what you see in the NSHipster picture.) be sure that you've got the class of the TableViewCell and the File's Owner set correctly on this. design away, create outlets as usual for your class.


ADDED IN LATER EDIT: also be sure you set the cells ReuseIdentifier to your class name (in my case, TableViewSectionHeaderCell).


(3) when you need a header in a UITableView from this nib, be sure to register it for whichever UITableView wants to use it in tableView.viewDidLoad, as in


override func viewDidLoad() {
  super.viewDidLoad()
   tableView.register(TableViewSectionHeaderCell.nib, forCellReuseIdentifier: TableViewSectionHeaderCell.reuseIdentifier)


be sure to define the nib and reuse identifier in the class as in NSHipster, although i found that making the reuseIdentifier a class var rather than a static let (in the NSHipster articale) seemed to work better:


class TableViewSectionHeaderCell: UITableViewCell {

class var reuseIdentifier: String {
  return String(describing: self)
}

class var nib: UINib {
  return UINib(nibName: String(describing: self), bundle: nil)
}

// plus whatever else you want


(4) override tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) to load your new UITableViewCell class, set it up, and then return the contentView of the cell (my section header just sets the title by filling in a label i have defined in the cell's nib)


  override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
  guard let cell = tableView.dequeueReusableCell(withIdentifier: TableViewSectionHeaderCell.identifier) as? TableViewSectionHeaderCell else {
  return nil
  }
  cell.sectionTitle!.text = sectionTitleDictionary[sectionList[section]]!
  return cell.contentView
  }


ADDED IN LATER EDIT: TableViewSectionHeaderCell.identifier should be TableViewSectionHeaderCell.reuseIdentifier above.


(cell.sectionTitle is a label and the expression sectionTitleDictionary[sectionList[section]]! is just how i find the title of a section.)


you'll want to set the header's height with tableView(_ tableView: UITableView, heightForHeaderInSection section: Int).



i think i got all the important code aspects covered above (despite some struggles getting the code editor to really work right) -- questions appreciated going forward!



hope that helps,

DMG

Hi DelawareMathGuy,

thank you for your clear explanation.

Till now we are moving around the cell without using UITableViewHeaderFooterView.

I tried more starting from your example.

First in creating the "header" swift file I noticed that creating a new file "Cocoa Touch Class" subclass of UITableViewHeaderFooterView the "Also create a xib file" is grayed out. Perhaps it cannot be a xib?

Anyway I created a UITableViewCell file with its xib file (flagging in the creation menu).

Then I modified the parent class from UITableViewCell to UITableViewHeaderFooterView; this require to remove setSelected().

import UIKit
class TableViewSectionHeaderCell: UITableViewHeaderFooterView {
  class var reuseIdentifier: String {
  return String(describing: self)
  }
  class var nib: UINib {
  return UINib(nibName: String(describing: self), bundle: nil)
  }
}

I also set TableViewSectionHeaderCell in "File's Owner"'s Class and its cell.

In the View Controller's class viewDidLoad I registered the "Header" with forHeaderFooterViewReuseIdentifier.

class myViewController: UIViewController
{
  @IBOutlet weak var myTableView: UITableView!
  override func viewDidLoad()
  {
  super.viewDidLoad()
  // Register the custom header view.
  myTableView.register(TableViewSectionHeaderCell.nib, forHeaderFooterViewReuseIdentifier: TableViewSectionHeaderCell.reuseIdentifier)
  }
}

finally I dequeue a "ReusableHeaderFooterView".

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
  {
  guard let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: TableViewSectionHeaderCell.reuseIdentifier) as? TableViewSectionHeaderCell
  else
  {
  return nil
  }
  return header
  }

In my current test I had a very simple "cell" with a coloured background and it works!

nice, i like it!


regards,

DMG