Chart Floorplan

The Chart Floorplan is a reusable component for visualizing analytic data. Users can interact with categories in multiple data series, view data summaries, and drill-down to data slices. Multiple chart types are supported: column, bar, line, and combination (column & line).

Two use cases for the Chart Floorplan are supported: full-screen (FUIChartFloorplanViewController, and partial-screen (FUIChartFloorplanTableViewCell). See the Fiori Design Guidelines for details of how each should be used within the application.

Usage

The core API of both the UIViewController and UITableViewCell variants is the FUIChartFloorplanView. The FUIChartFloorplanView exposes a set of title properties directly, for filling in content like the floorplan title, subtitle, status, seriesTitles, valuesAxisTitle, and categoryAxisTitle. It also exposes a FUIChartView, and FUIChartSummaryView which have their own sub-APIs.

class AnalyticFloorplanComboChartVC: FUIChartFloorplanViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // MARK: - configuring view controller & chart view properties

        self.titleText.text = "Monthly APE"
        self.status.text = "Updated 20 min ago"
        self.subtitle.text = "Month Target: 200k"
        self.chartView.chartType = .combo
        self.chartView.categoryAxis.labelLayoutStyle = .range
        self.chartView.dataSource = self
        self.valuesAxisTitle.text = "APE"
        self.categoryAxisTitle.text = "Month"
        self.seriesTitles = chartSeriesTitles().map { FUIText(stringLiteral: $0)}

        // MARK: - configuring summary view properties & setting 'aggregate' summary item

        self.headerView.dataSource = self
        self.headerView.delegate = self
        self.headerView.seriesTitleText = chartSeriesTitles().map { FUIText(stringLiteral: $0)}

        let item = FUIChartSummaryItem()
        item.categoryIndex = -1     // will pin to left
        item.isEnabled = false      // is not tappable

        let values: [Double] = {
            var values: [Double] = []
            let seriesCount = chartData().count
            for i in 0..<seriesCount {
                values.append(chartData()[i].map({ $0 * 1000 }).suffix(from: 1).reduce(0, +))
            }
            return values
        }()

        item.valuesText = values.map { formattedTitleForDouble($0)! }
        item.title.text = "TTM"
        self.headerView.addItem(item)

FUIChartViewDataSource

The FUIChartViewDataSource supplies data to the FUIChartView. Data is partitioned into series (for example, all data from 2016, and all data from 2017), which are then partioned into categories (for example, all data from January, and all data from February).

The data source supplies the values and formatted meta-text to the chart, for the given number of series & categories.

For example:

extension AnalyticFloorplanComboChartVC: FUIChartViewDataSource {

    // MARK: - FUIChartViewDataSource methods
    func numberOfSeries(in: FUIChartView) -> Int {
        return chartData().count
    }

    func chartView(_ chartView: FUIChartView, numberOfValuesInSeries seriesIndex: Int) -> Int {
        return chartData()[seriesIndex].count
    }

    func chartView(_ chartView: FUIChartView, valueForSeries series: Int, category categoryIndex: Int, dimension dimensionIndex: Int) -> Double? {
        return chartData()[series][categoryIndex]
    }

    func chartView(_ chartView: FUIChartView, formattedStringForValue value: Double, axis: FUIChartAxisId) -> String? {
        return formattedTitleForDouble(value)
    }

    func chartView(_ chartView: FUIChartView, titleForCategory categoryIndex: Int, inSeries seriesIndex: Int) -> String? {
        return chartCategoryTitles()[categoryIndex]
    }

    // MARK: - helper methods for generating & formatting sample data

    func chartSeriesTitles() -> [String] {
        return ["Actual", "Target"]
    }
    func chartCategoryTitles() -> [String] {
        return ["Jan-17", "Feb-17", "Mar-17", "Apr-17", "May-17", "Jun-17", "Jul-17", "Aug-17", "Sep-17", "Oct-17", "Nov-17", "Dec-17", "Jan-18"]
    }

    func chartData() -> [[Double]] {
        return [[170, 140, 150, 161, 190, 168, 150, 170, 145, 167, 210, 220, 180], [150, 160, 160, 160, 160, 160, 160, 180, 180, 180, 200, 200, 200]]
    }

    func formattedTitleForDouble(_ value: Double) -> String? {
        let numberFormatter = NumberFormatter()
        numberFormatter.numberStyle = .currencyAccounting
        numberFormatter.maximumFractionDigits = 0
        return numberFormatter.string(from: value as NSNumber)
    }
}

Note: Developers should configure the chart view, then set the dataSource property - or, should invoke reloadData() after modifying the configurations of the chart view.

FUIChartSummaryDataSource

The FUIChartSummaryDataSource supplies an optional FUIChartSummaryItem, to be displayed or updated when the user taps on a category in the chart. This summary item may be set to tappable using the isEnabled: Bool property, so that a user can select the summary item to drill-down into the data slice for the category. These summary items may also display trend information across the different series captured in the category. Empty text is permitted, and an emptyText: FUIText value may be filled-in for nil data values.

extension AnalyticFloorplanComboChartVC: FUIChartSummaryDataSource {

    func chartView(_ chartView: FUIChartView, summaryItemForCategory categoryIndex: Int) -> FUIChartSummaryItem? {
        let item = FUIChartSummaryItem()
        item.categoryIndex = categoryIndex
        item.isEnabled = true

        let values: [Double] = {
            var values: [Double] = []
            let seriesCount = chartData().count
            for i in 0..<seriesCount {
                values.append(chartData()[i][categoryIndex] * 1000.0)
            }
            return values
        }()

        let deltaPercent = values.first! / values.last! - 1
        let red = UIColor.preferredFioriColor(forStyle: .negative, background: .lightBackground)
        let green = UIColor.preferredFioriColor(forStyle: .positive, background: .lightBackground)
        let percentFormatter = NumberFormatter()
        percentFormatter.numberStyle = .percent

        item.valuesText = values.map { formattedTitleForDouble($0)! }
        item.title.text = chartCategoryTitles()[categoryIndex]
        item.trend.text = deltaPercent != 0 ? percentFormatter.string(from: deltaPercent as NSNumber)! : nil
        switch deltaPercent {
        case ..<0:
            item.trendImage = FUIIconLibrary.analytics.trendDown.withRenderingMode(.alwaysTemplate)
            item.trendSemanticColor = red
        case 0:
            item.trendImage = nil
            item.trendSemanticColor = nil
        case 0.0001...:
            item.trendImage = FUIIconLibrary.analytics.trendUp.withRenderingMode(.alwaysTemplate)
            item.trendSemanticColor = green
        default:
            break
        }
        return item
    }
}

FUIChartSummaryDelegate

The FUIChartSummaryDelegate is invoked, when the user taps on an enabled FUIChartSummaryItemView. The developer should push a new FUIChartFloorplanViewController, to render the data slice for the selected category.

extension AnalyticFloorplanComboChartVC : FUIChartSummaryDelegate {
    func chartView(_ chartView: FUIChartView, didSelectCategorySummaryItem summaryItem: FUIChartSummaryItem) {
        let floorplan = SalespersonAnalyticsViewController(month: summaryItem.categoryIndex)
        self.navigationController?.pushViewController(floorplan, animated: true)
    }
}
  • UIViewController implementation of the FUIChartFloorplanView. Should be used when displaying the full-scren Chart Floorplan of the design guidelines.

    Extends the API of FUIChartFloorplanView.

    ## Usage
    self.title = "Sales Chart"
    self.chartView.chartType = .bar
    self.chartView.numberOfGridlines = 4
    self.chartView.dataSource = self
    
    self.headerView.dataSource = self
    self.titleText.text = "Total APE ($) by Salesperson"
    self.status.text = "Updated 20m ago"
    self.categoryAxisTitle.text = "Salesperson"
    self.valuesAxisTitle.text = "Total APE ($)"
    
    let item = FUIChartSummaryItem()
    item.categoryIndex = -1
    item.isEnabled = false
    item.isPreservingTrendHeight = false
    
    let values: [Double] = {
    var values: [Double] = []
    for series in chartView.series {
        let categoriesUpperBound = series.numberOfValues - 1
        if let valuesInSeries = series.valuesInCategoryRange((0...categoriesUpperBound), dimension: 0) {
           values.append(valuesInSeries.flatMap({ $0 }).reduce(0.0, +))
           }
        }
        return values
    }()
    
    let numberFormatter  = NumberFormatter()
    numberFormatter.numberStyle = .currency
    numberFormatter.maximumFractionDigits = 0
    
    
    item.valuesText = values.map { "\(numberFormatter.string(from: $0 as NSNumber)!)k" }
    item.titleText.text = "Team ($) APE"
    self.headerView.addItem(item)
    

    Theming

    Supported class paths:

    fdlFUIChartFloorplanViewController_title {}
    fdlFUIChartFloorplanViewController_subtitle {}
    fdlFUIChartFloorplanViewController_status {}
    fdlFUIChartFloorplanViewController_seriesTitles {}
    fdlFUIChartFloorplanViewController_valuesAxisTitle {}
    fdlFUIChartFloorplanViewController_categoryAxisTitle {}
    

    Supported properties:

    font-color: Color;
    font-style: UIFontTextStyle;
    text-line-clamp: Integer;
    text-align: NSTextAlignment;
    
    See more

    Declaration

    Swift

    open class FUIChartFloorplanViewController : FUIBaseDrawingViewController<FUIChartFloorplanView>, FUIBackgroundSchemeSupporting
  • UITableViewCell container of FUIChartFloorplanView. Should be used when displaying the Chart Floorplan in a non-full-screen context (e.g. Overview Floorplan).

    Extends the API of FUIChartFloorplanView.

    Usage

    let cell = tableView.dequeueReusableCell(withIdentifier: FUIChartFloorplanTableViewCell.reuseIdentifier, for: indexPath) as! FUIChartFloorplanTableViewCell
    
    cell.title.text = titleItem.title
    cell.subtitle.text = titleItem.subtitle
    cell.status.text = titleItem.status
    
    cell.valuesAxisTitle.text = "Sum ($) of Sales"
    cell.categoryAxisTitle.text = "Month"
    cell.chartView.chartType = .column
    cell.chartView.dataSource = dataItem.dataSeries
    
    let seriesTitles: [FUIText] = ["2017"]
    cell.seriesTitles = seriesTitles
    
    cell.summaryView.dataSource = self
    cell.chartView.select(category: 2)
    
    cell.summaryView.seriesTitleText = seriesTitles
    
    return cell
    

    Theming

    Supported class paths:

    fdlFUIChartFloorplanTableViewCell_title {}
    fdlFUIChartFloorplanTableViewCell_subtitle {}
    fdlFUIChartFloorplanTableViewCell_status {}
    fdlFUIChartFloorplanTableViewCell_seriesTitles {}
    fdlFUIChartFloorplanTableViewCell_valuesAxisTitle {}
    fdlFUIChartFloorplanTableViewCell_categoryAxisTitle {}
    

    Supported properties:

    font-color: Color;
    font-style: UIFontTextStyle;
    text-line-clamp: Integer;
    text-align: NSTextAlignment;
    
    See more

    Declaration

    Swift

    open class FUIChartFloorplanTableViewCell : FUIBaseDrawingTableViewCell<FUIChartFloorplanView>
  • View implementation of FUIChartFloorplan. Shared by FUIChartFloorplanViewController and FUIChartFloorplanTableViewCell.

    Composed of FUIChartTitleView, FUIChartSummaryView, FUIChartPlotView, and FUIChartLegendView. The FUIChartView of the FUIChartPlotView is exposed as a public property, and should be used directly by the developer.

    Data Sources

    Developer should implement the FUIChartViewDataSource to supply data to the FUIChartView.

    Developer should implement the FUIChartSummaryDataSource, to supply FUIChartSummaryItem instances to be displayed when users select categories in the chart view.

    Delegates

    Developer may optionally implement the FUIChartSummaryDelegate, to handle selection of the FUIChartSummaryItem(s).

    Usage

    self.title = "Sales Chart"
    self.chartView.chartType = .bar
    self.chartView.numberOfGridlines = 4
    self.chartView.dataSource = self
    
    self.summaryView.dataSource = self
    self.titleText.text = "Total APE ($) by Salesperson"
    self.status.text = "Updated 20m ago"
    self.categoryAxisTitle.text = "Salesperson"
    self.valuesAxisTitle.text = "Total APE ($)"
    
    let item = FUIChartSummaryItem()
    item.categoryIndex = -1
    item.isEnabled = false
    item.isPreservingTrendHeight = false
    
    let values: [Double] = {
        var values: [Double] = []
        for series in chartView.series {
            let categoriesUpperBound = series.numberOfValues - 1
            if let valuesInSeries = series.valuesInCategoryRange((0...categoriesUpperBound), dimension: 0) {
                values.append(valuesInSeries.flatMap({ $0 }).reduce(0.0, +))
            }
        }
        return values
    }()
    
    let numberFormatter  = NumberFormatter()
    numberFormatter.numberStyle = .currency
    numberFormatter.maximumFractionDigits = 0
    
    item.valuesText = values.map { "\(numberFormatter.string(from: $0 as NSNumber)!)k" }
    item.titleText.text = "Team ($) APE"
    self.summaryView.addItem(item)
    

    Theming

    Supported class paths:

    fdlFUIChartFloorplanView_title {}
    fdlFUIChartFloorplanView_subtitle {}
    fdlFUIChartFloorplanView_status {}
    fdlFUIChartFloorplanView_seriesTitles {}
    fdlFUIChartFloorplanView_valuesAxisTitle {}
    fdlFUIChartFloorplanView_categoryAxisTitle {}
    

    Supported properties:

    font-color: Color;
    font-style: UIFontTextStyle;
    text-line-clamp: Integer;
    text-align: NSTextAlignment;
    
    See more
  • Title component of Chart Floorplan. Typically not used directly by developer.

    Usage

    let cell = tableView.dequeueReusableCell(withIdentifier: FUIChartTitleTableViewCell.reuseIdentifier, for: indexPath) as! FUIChartTitleTableViewCell
    
    cell.title.text = "Sum of Sales ($) by Month, Quarter, and Year"
    cell.subtitle.text = "2016-2017"
    cell.status.text = "Updated 3 hours ago"
    
    return cell
    

    Theming

    Supported class paths:

    fdlFUIChartTitleTableViewCell_title {}
    fdlFUIChartTitleTableViewCell_subtitle {}
    fdlFUIChartTitleTableViewCell_status {}
    

    Supported properties:

    font-color: Color;
    font-style: UIFontTextStyle;
    text-line-clamp: Integer;
    text-align: NSTextAlignment;
    
    See more

    Declaration

    Swift

    @IBDesignable
    open class FUIChartTitleTableViewCell : FUIBaseDrawingTableViewCell<FUIChartTitleView>
  • Summary content view of Chart Floorplan, and related components.

    Data Source

    Developer should implement FUIChartSummaryDataSource to supply a summary item when user selects a category in the FUIChartFloorplan views.

    Often, the Developer will want to pin an ‘aggregate’ summary item to the left of the summary view, which might sum values for each series over the range of categories in the chart, display the macro trend, etc. For this case, return an item for categoryIndex == -1.

    Delegate

    Developer may also optionally implement FUIChartSummaryDelegate, to handle user taps on summary items in the FUIChartSummaryView. According to the design guidelines, this should typically drill-down into the data slice for the selected category(ies).

    Usage

    let item = FUIChartSummaryItem()
    
    // Category index `-1` used for 'aggregate' column.  True category indexes are requested by `dataSource`.
    item.categoryIndex = -1
    item.isEnabled = false
    
    let values: [Double] = {
    var values: [Double] = []
    let seriesCount = chartData().count
       for i in 0..<seriesCount {
           values.append(chartData()[i].map({ $0 * 1000 }).suffix(from: 1).reduce(0, +))
       }
        return values
    }()
    
    item.valuesText = values.map { formattedTitleForDouble($0)! }
    item.title.text = "TTM"
    
    // If `chartView != nil`, supply an item to the `dataSource`.  Otherwise, add category index to selected set.
    summaryView.addItem(at: item.category)
    

    Attention

    The delegate object with type FUIChartSummaryDelegate is declared as a weak reference. So on deallocation it will be automatically set to nil. To keep it alive as expected, developer should retain the delegate object during its whole execution scope.

    See more

    Declaration

    Swift

    open class FUIChartSummaryView : FUIDrawingView, ChartObserver
  • Data source for FUIChartSummaryView. Optionally, supply a summary item for the category.

    Important

    the FUIChartSummaryDataSource will query for a summary item for categoryIndex = -1. This item will be displayed at the far left, and should typically contain aggregate info.
    See more

    Declaration

    Swift

    public protocol FUIChartSummaryDataSource : AnyObject
  • Delegate for FUIChartSummaryView. Optionally, supply a selection handler for taps on the category item in the Summary view.

    See more

    Declaration

    Swift

    public protocol FUIChartSummaryDelegate : AnyObject
  • Item summarizing the category values of series in a chart. Should be supplied to FUIChartSummaryDataSource, or, if FUIChartSummaryView.chartView == nil, set to the FUIChartSummaryView using addItem(at:).

    Usage

    let item = FUIChartSummaryItem()
    
    // Category index `-1` used for 'aggregate' column. `dataSource` will request item at `-1`,
    // and for indexes selected in the `chartView`, or, if `chartview == nil`,
    // for indexes added using `addItem(at:)`.
    item.categoryIndex = -1
    item.isEnabled = false
    
    let values: [Double] = {
        var values: [Double] = []
        let seriesCount = chartData().count
        for i in 0..<seriesCount {
           values.append(chartData()[i].map({ $0 * 1000 }).suffix(from: 1).reduce(0, +))
        }
        return values
    }()
    
    item.valuesText = values.map { formattedTitleForDouble($0)! }
    item.title.text = "TTM"
    
    See more

    Declaration

    Swift

    public class FUIChartSummaryItem : FUIChartSummaryItemType
  • Undocumented

    Declaration

    Swift

    public class FUIChartSummaryItemType
  • Item summarizing the category values of series in a chart. Should be supplied to FUIChartSummaryDataSource, or, if FUIChartSummaryView.chartView == nil, set to the FUIChartSummaryView using addItem(at:).

    Usage

    let item = FUIChartSummaryItem()
    
    // Category index `-1` used for 'aggregate' column. `dataSource` will request item at `-1`,
    // and for indexes selected in the `chartView`, or, if `chartview == nil`,
    // for indexes added using `addItem(at:)`.
    item.categoryIndex = -1
    item.isEnabled = false
    
    let values: [Double] = {
    var values: [Double] = []
    let seriesCount = chartData().count
    for i in 0..<seriesCount {
    values.append(chartData()[i].map({ $0 * 1000 }).suffix(from: 1).reduce(0, +))
    }
    return values
    }()
    
    item.valuesText = values.map { formattedTitleForDouble($0)! }
    item.title.text = "TTM"
    
    See more

    Declaration

    Swift

    public class FUIChartRangeSummaryItem : FUIChartSummaryItemType
  • Plot component of Chart Floorplan. May be used stand-alone, with other FUIChartFloorplan* cells, outside of the Chart Floorplan.

    Usage

    let cell = tableView.dequeueReusableCell(withIdentifier: FUIChartPlotTableViewCell.reuseIdentifier, for: indexPath) as! FUIChartPlotTableViewCell
    cell.valuesAxisTitle.text = "Salesperson"
    cell.categoryAxisTitle.text = "Sum of Sales ($)"
    cell.chartView.categoryAxis.labelLayoutStyle = .range
    cell.chartView.dataSource = self
    return cell
    

    Theming

    Supported class paths:

    fdlFUIChartPlotTableViewCell_valuesAxisTitle {}
    fdlFUIChartPlotTableViewCell_categoryAxisTitle {}
    

    Supported properties:

    font-color: Color;
    font-style: UIFontTextStyle;
    text-line-clamp: Integer;
    text-align: NSTextAlignment;
    
    See more

    Declaration

    Swift

    @IBDesignable
    open class FUIChartPlotTableViewCell : FUIBaseDrawingTableViewCell<FUIChartPlotView>
  • Plot content view of Chart Floorplan, and related components.

    Usage

    let view = FUIChartPlotView()
    view.valuesAxisTitle.text = "Salesperson"
    view.categoryAxisTitle.text = "Sum of Sales ($)"
    view.chartView.categoryAxis.labelLayoutStyle = .range
    view.chartView.dataSource = self
    

    Theming

    Supported class paths:

    fdlFUIChartPlotView_valuesAxisTitle {}
    fdlFUIChartPlotView_categoryAxisTitle {}
    

    Supported properties:

    font-color: Color;
    font-style: UIFontTextStyle;
    text-line-clamp: Integer;
    text-align: NSTextAlignment;
    
    See more

    Declaration

    Swift

    open class FUIChartPlotView : FUIDrawingView, FUIAxisTitleComponent, ChartObserver
  • Legend component of Chart Floorplan. May be used stand-alone, with other FUIChartFloorplan* cells, outside of the Chart Floorplan.

    Usage

    let cell = tableView.dequeueReusableCell(withIdentifier: FUIChartLegendTableViewCell.reuseIdentifier, for: indexPath) as! FUIChartLegendTableViewCell
    cell.seriesTitles = ["2016", "2017"]
    cell.seriesColor = [UIColor.preferredFioriColor(forStyle: .chart1), UIColor.preferredFioriColor(forStyle: .chart2)]
    return cell
    

    Theming

    Supported class paths:

    fdlFUIChartLegendTableViewCell_seriesTitles {}
    

    Supported properties:

    font-color: Color;
    font-style: UIFontTextStyle;
    text-line-clamp: Integer;
    text-align: NSTextAlignment;
    
    See more

    Declaration

    Swift

    @IBDesignable
    open class FUIChartLegendTableViewCell : FUIBaseDrawingTableViewCell<FUIChartLegendView>