Implementation of Adapter pattern to add to UITableView supporting to show hierarchical data structures
Quick start:
- Just conform your model to TreeNode protocol and it can be displayed in UITableView hierarchically
- Implement custom TableViewCell
- Declare Adapter property in your UIViewController
- Implement UITableViewDelegate and UITableViewDatasource using Adapter
Usage
Comform your CellViewModel to TreeNode protocol as a FolderCellViewModel class
public protocol TreeNode { associatedtype T var opened: Bool { get set } var level: Int { get set } var hasChildren: Bool { get } func children <T where T: TreeNode> () -> [T] }
class FolderCellViewModel: TreeNode { typealias T = FolderCellViewModel init(ident: String) { self.ident = ident } var ident: String var name: String? var subFolders = [FolderCellViewModel]() var opened: Bool = false var level: Int = 0 var hasChildren: Bool { return subFolders.count > 0 } func children<FolderCellViewModel>() -> [FolderCellViewModel] { return subFolders.flatMap { $0 as? FolderCellViewModel } } }
Implement custom tableview cell
class FolderTableViewCell: UITableViewCell { @IBOutlet weak var label: UILabel! @IBOutlet weak var folderImageView: UIImageView! @IBOutlet weak var labelOffsetConstraint: NSLayoutConstraint! var folderViewModel: FolderCellViewModel? { didSet { fill() } } func fill() { guard let folder = folderViewModel else { return } label?.text = folder.name labelOffsetConstraint.constant = CGFloat(folder.level * 16) folderImageView.image = nil backgroundColor = NodeStyles.leafColor guard folder.hasChildren else { return } backgroundColor = NodeStyles.parentColor folderImageView.image = folder.opened ? NodeStyles.openedImage : NodeStyles.closedImage } } struct NodeStyles { static let parentColor: UIColor = { return UIColor.whiteColor() }() static let leafColor: UIColor = { return UIColor.whiteColor() }() static let openedImage: UIImage? = { return UIImage(named: "li_up") }() static let closedImage: UIImage? = { return UIImage(named: "li_down") }() }
Declare Adapter
var folderAdapter = TreeTableViewAdapter<FolderCellViewModel>()
Implement UITableViewDelegate and UITableViewDatasource using Adapter
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return folderAdapter.numberOfRows } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { guard let cellViewModel = folderAdapter.node(forIndexPath: indexPath), let cell = tableView.dequeueReusableCellWithIdentifier(cellViewModel.ident) as? FolderTableViewCell else { return cellNotFound() } cell.folderViewModel = cellViewModel return cell } func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { guard let cellViewModel = folderAdapter.node(forIndexPath: indexPath) where cellViewModel.hasChildren else { return } folderAdapter.changeNodeState(inTableView: tableView, atIndexPath: indexPath) }
Set your model array to Adapter and reload data in UITableView
folderAdapter.nodes = [YOUR_MODEL] tableView.reloadData()
Installation
Carthage:
github "dtsvz/TreeTableViewAdapter" == 1.0.1
Or you can manually add TreeTableViewAdapter.swift file to your project
Requirements
- iOS 8.0
- XCode 7.3
- Swift 2.2

