FUIHierarchyView

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.

HierarchyView

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.

HierarchyView

HierarchyView

Swipe

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

HierarchyView

HierarchyView

Pan

Pan left/right to navigate further in hierarchy view.

HierarchyView

Usage

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 {
            return
        }

        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 = object.name
    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()

Theming

Supported class paths:

fdlFUIHierarchyView {}
fdlFUIHierarchyView_header {}
  • The header displayed on the top of hierarchy view. (Only display when horizontalSizeClass equals to compact)

    Declaration

    Swift

    public let header: FUIHierarchyViewHeader
  • The data source object of hierarchy view.

    Declaration

    Swift

    public weak var dataSource: FUIHierarchyViewDataSource?
  • The delegate object fo hierarchy view.

    Declaration

    Swift

    public weak var delegate: FUIHierarchyViewDelegate?
  • A Boolean value that determines whether the hierarchy view is in editing mode.

    Declaration

    Swift

    public var isEditing: Bool { get set }
  • A Boolean value that determines whether users can select cells while the hierarchy view is in editing mode. By default, only single selection is enable when allowsSelectionDuringEditing is set true and allowsMultipleSelectionDuringEditing is set to false. Prerequisite: isEditing should be true.

    Declaration

    Swift

    public var allowsSelectionDuringEditing: Bool { get set }
  • A Boolean value that controls whether users can select more than one cell simultaneously in editing mode. Prerequisite: isEditing should be true.

    Declaration

    Swift

    public var allowsMultipleSelectionDuringEditing: Bool { get set }
  • List of UUIDs for selected items.

    Declaration

    Swift

    public var selectedUUIDs: [String] { get }
  • Register a class for use in creating new collection item cells.

    Declaration

    Swift

    open func register(_ cellClass: AnyClass?, forCellWithReuseIdentifier identifier: String)

    Parameters

    cellClass

    The class of a cell that you want to use in the hierarchy view.

    identifier

    The reuse identifier to associate with the specified class. This parameter must not be nil and must not be an empty string.

  • Returns a reusable cell object for an collection item with specified uuid.

    Declaration

    Swift

    public func dequeueReusableCell(withReuseIdentifier identifier: String, with uuid: String) -> FUIHierarchyCollectionItem

    Parameters

    identifier

    The reuse identifier for the specified cell. This parameter must not be nil.

    uuid

    The uuid of the item which the cell associated with.

    Return Value

    A FUIHierarchyViewCollectionItem object.

  • Returns the visible collection item cell for the item with uuid.

    Declaration

    Swift

    public func cellForItem(with uuid: String) -> FUIHierarchyCollectionItem?

    Parameters

    uuid

    The uuid of the item which the cell associated with.

    Return Value

    A FUIHierarchyViewCollectionItem object or nil if the cell is not visible or uuid does not exists in hierarchy view.

  • Reloads all of the data for hierarchy view.

    Call this method when you want to reload all the item in hierarchy view. This causes hierarchy view to recalculate the positions and sizes for all items and recreate cells for them. This is an expensive operation so only call this when needed.

    Declaration

    Swift

    open func reloadData()
  • Insert children located at certain indexes into a parent. The parent must be in expanded state.

    Declaration

    Swift

    open func insertChildren(of parent: String, at indexes: IndexSet)

    Parameters

    parent

    The parent that gets new children.

    indexes

    The indexes at which to insert children.

  • Remove children located at specified indexes from a parent. The parent must be in expanded state.

    Declaration

    Swift

    open func removeChildren(of parent: String, at indexes: IndexSet)

    Parameters

    parent

    The parent from which children are removed.

    indexes

    The indexes of children that are removed.

  • Invalidate parent of the child specified. The child must be in expanded state.

    Call this method when you need to add a new parent(the child has no parent previously), remove the existing parent or replace the parent of an child item. Keep in mind the sibling of the child whose parent gets invalidated will also be removed, you can call insertChildren(of:at:) to add children of the new parent into the hierarchy view if needed.

    Declaration

    Swift

    open func invalidateParent(of child: String)

    Parameters

    child

    The child whose parent is invalidated.

  • Invalidate children of the parent specified. The parent must be in expanded state.

    Call this method when you need to update children of a parent.

    Declaration

    Swift

    open func invalidateChildren(of parent: String)

    Parameters

    parent

    The parent whose children are invalidated.

  • Invalidate hierarchy items with specified uuids.

    Calling this method will ask hierarchy view to reload cells for the items with specified uuids. This method only causes the cells associated with those items to be discarded and redisplay. UUIDs of those items should not change during invalidation.

    Declaration

    Swift

    open func invalidateItems(with uuids: [String])

    Parameters

    uuids

    The uuids of hierarchy items who are invalidated.