19

I am trying to set up an UITableView with sections using the new UITableViewDiffableDataSource within an UITableViewController.

Everything seems to work fine except setting the section header titles.

According to Apple's documentation, UITableViewDiffableDataSource conforms to UITableViewDataSource, so I was expecting this to be possible.

I have tried:

  1. overriding tableView(_ tableView:, titleForHeaderInSection section:) in the UITableViewController class
  2. subclassing UITableViewDiffableDataSource and implementing tableView(_ tableView:, titleForHeaderInSection section:) in the subclass

but both ways lead to no result (Xcode 11 and iOS13 beta 3).

Is there currently a way to set the section header titles using UITableViewDiffableDataSource?

mmklug
  • 2,252
  • 2
  • 16
  • 31

4 Answers4

18

Providing code example on @particleman's explanations.

struct User: Hashable {
    var name: String
}

enum UserSection: String {
    case platinum = "Platinum Tier"
    case gold = "Gold Tier"
    case silver = "Silver Tier"
}

class UserTableViewDiffibleDataSource: UITableViewDiffableDataSource<UserSection, User> {
    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        guard let user = self.itemIdentifier(for: IndexPath(item: 0, section: section)) else { return nil }
        return self.snapshot().sectionIdentifier(containingItem: user)?.rawValue
    }
}
Brandon WongKS
  • 543
  • 6
  • 14
14

Update: Starting in beta 8, you can now implement tableView(_ tableView:, titleForHeaderInSection section:) in a UITableViewDiffableDataSource subclass and it works properly.

The default behavior for populating the header title has always been a little strange to have in the data source. With UITableViewDiffableDataSource, Apple seems to be acknowledging such by not providing the default string-based behavior; however, the UITableViewDelegate methods continue to work as before. Implementing tableView(_:viewForHeaderInSection:) by initializing and returning a UILabel with the desired section title and implementing tableView(_:heightForHeaderInSection:) to manage the desired height works.

particleman
  • 635
  • 1
  • 7
  • 12
  • I'm targeting iOS 13 and developing in Xcode 11.4.1 and sadly it looks the `tableView(_:viewForHeaderInSection:)` method is not available to be overridden in extensions of `UITableViewDiffableDataSource` – Adil Hussain Apr 27 '20 at 13:59
  • I've posted a question about this in the Apple Developer forums [here](https://forums.developer.apple.com/message/416881) in the unlikely hope that an Apple engineer will see it. – Adil Hussain Apr 27 '20 at 14:37
  • 1
    So it turns out the `tableView(_:viewForHeaderInSection:)` method is provided by `UITableViewDelegate` and not `UITableViewDataSource`. My bad! – Adil Hussain Apr 28 '20 at 09:14
0

Let me suggest quite a flexible universal solution:

Declare a subclass:

class StringConvertibleSectionTableViewDiffibleDataSource<UserSection: Hashable, User: Hashable>: UITableViewDiffableDataSource<UserSection, User> where UserSection: CustomStringConvertible {
    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return sectionIdentifier(for: section)?.description
    }
}

Usage example:

class ComitsListViewController: UITableViewController {

private var diffableDataSource = StringConvertibleSectionTableViewDiffibleDataSource<String, Commit>(tableView: tableView) { (tableView, indexPath, commit) -> UITableViewCell? in
            let cell = tableView.dequeueReusableCell(withIdentifier: "Commit", for: indexPath)
            cell.configure(with: commit)
            return cell
        }

}

You are not limited just to String thought. You can control what to display as section title by implementing description var of CustomStringConvertible protocol for your section type.

Paul B
  • 3,989
  • 33
  • 46
-3

After you init self.dataSource to a UITableViewDiffableDataSource (which sets itself to the tableView.dataSource) set the tableView.dataSource back to self, i.e. the UITableViewController subclass. Now in your numberOfSectionsInTableView and numberOfRowsInSection methods forward those to self.dataSource and return its info (this is the composition pattern). Now your UITableViewController just implements its section titles as normal since it is the table's data source.

I believe UITableViewDiffableDataSource should not be setting itself as the dataSource if one is already set but I guess they designed it to work in the least error prone way because with UITableViewController added to a storyboard its already set.

If you do it this way then it makes sense why the class wasn't open in the early iOS 13 betas.

malhal
  • 26,330
  • 7
  • 115
  • 133