Skip to content

Hierarchy View


open class FUIHierarchyView: UIView, FUIContentCopyable

A view that manages a collection of items in a multi-column layout.

Initial Loading Process

  1. Hierarchy view asks data source for a root item and put it in the first column. Hierarchy view will be empty if nil is returned for root. If so, hierarchy view finishes loading.
  2. Hierarchy view asks data source for number of children of the root and get each child item iteratively.
  3. Hierarchy view asks data source for the parent of root item. If that parent exists, insert it into preview column before the root.


Interaction Behavior

Expand Children

Tap on the hierarchy accessory view will drill down one level in the hierarchy view to show the children of tapped item.




Quick swipe left/right to view the previous or next level in the hierarchy.




Pan left/right to navigate further in hierarchy view.



Have your data source object conform to FUIHierarchyViewDataSource protocol and set it to hierarchy view's dataSource property.

class YourHierarchyViewDataSource: FUIHierarchyViewDataSource {}

hierarchyView.dataSource = YourHierarchyViewDataSource()

Register your custom cell if needed. Otherwise hierarchy view will use FUIHierarchyCollectionItem by default.

hierarchyView.register(YourCustomCollectionViewCell.self, forCellWithReuseIdentifier: "ReuseIdentifierForYourCustomCell")

Implement dataSource methods.

// Define your data model.
var model = HierarchyViewModel()

func rootUUID(in hierarchyView: FUIHierarchyView) -> String? {
    return model.rootObject?.uuid

func hierarchyView(_ hierarchyView: FUIHierarchyView, numberOfChildrenForItemWith uuid: String) -> Int {
    guard let object = model.getObject(for: uuid) else {
        preconditionFailure("Object with String: \(uuid) does not exsit in model")

    if let children = object.children {
        return children.count

    // Get children asynchroniously if they haven't been loaded yet. When loading completes, cache those children for future access and insert children into hierarchy view by calling `insertChildren(of:at:)`.
    object.asyncFetchChildren { children in
        self.model.updateCache(objects: children)
        self.hierarchyView.insertChildren(of: uuid, at: IndexSet(integersIn: 0..<children.count))

    // Return zero children before children is loaded.
    return 0

func hierarchyView(_ hierarchyView: FUIHierarchyView, uuidForChildItemAt index: Int, with parentUUID: String) -> String {
    guard let parent = model.getObject(for: parentUUID) else {
        preconditionFailure("Parent object with String: \(parentUUID) does not exsit")

    guard let children = parent.children else {
        preconditionFailure("Children of parent: \(parent) is not loaded yet")

    return children[index].uuid

func hierarchyView(_ hierarchyView: FUIHierarchyView, parentForItemWith uuid: String) -> String? {
    guard let object = model.getObject(for: uuid) else {
        preconditionFailure("Object with String: \(uuid) does not exsit in model")

    if let parent = object.parent {
        return parent.uuid

    // Get parent asynchronously if they haven't been loaded yet. When loading completes, cache the parent for future access and insert it into hierarchy view by calling `invalidateParent(of:)`.
    object.asyncFetchParent { parent in
        guard let parent = parent else {

        self.model.updateCache(objects: [parent])
        self.model.updateRootObject(newRootObject: parent)
        self.hierarchyView.invalidateParent(of: uuid)

    // Return nil before parent is loaded.
    return nil

func hierarchyView(_ hierarchyView: FUIHierarchyView, cellForItemWith uuid: String) -> FUIHierarchyCollectionItem {
    let cell = hierarchyView.dequeueReusableCell(withReuseIdentifier: ReuseIdentifierForYourCustomCell, with: uuid) as! YourCustomCollectionViewCell
    guard let object = model.getObject(for: uuid) else {
        return cell

    cell.title.text =
    cell.subtitle.text = object.type
    cell.body.text = object.location
    cell.accessoryType = .disclosureIndicator
    if object.numberOfChildren > 0 {
        cell.isHierarchyButtonHidden = false
        cell.hierarchyAttributeText = String(object.numberOfChildren)
    else {
        cell.isHierarchyButtonHidden = true

    return cell

func hierarchyView(_ hierarchyView: FUIHierarchyView, titleForItemWith uuid: String) -> String? {
    return model.getObject(for: uuid)?.name

Make your delegate object conform to FUIHierarchyViewDelegate protocol and assign it to hierarchy view's delegate property if needed.

class YourHierarchyViewDelegate: FUIHierarchyViewDelegate {}

hierarchyView.delegate = YourHierarchyViewDelegate()


Supported class paths:

fdlFUIHierarchyView {}
fdlFUIHierarchyView_header {}

Last update: April 14, 2021