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 partioned into series (e.g. all data from 2016
, and all data from 2017
), which are then partioned into categories (e.g. 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.
E.g.:
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 dat
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 invokereloadData()
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 theFUIChartFloorplanView
. 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:
See morefont-color: Color; font-style: UIFontTextStyle; text-line-clamp: Integer; text-align: NSTextAlignment;
Declaration
Swift
open class FUIChartFloorplanViewController : FUIBaseDrawingViewController<FUIChartFloorplanView>, FUIBackgroundSchemeSupporting
-
UITableViewCell
container ofFUIChartFloorplanView
. 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:
See morefont-color: Color; font-style: UIFontTextStyle; text-line-clamp: Integer; text-align: NSTextAlignment;
Declaration
Swift
open class FUIChartFloorplanTableViewCell : FUIBaseDrawingTableViewCell<FUIChartFloorplanView>
-
View implementation of
FUIChartFloorplan
. Shared byFUIChartFloorplanViewController
andFUIChartFloorplanTableViewCell
.Composed of
FUIChartTitleView
,FUIChartSummaryView
,FUIChartPlotView
, andFUIChartLegendView
. TheFUIChartView
of theFUIChartPlotView
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 theFUIChartView
.Developer should implement the
FUIChartSummaryDataSource
, to supplyFUIChartSummaryItem
instances to be displayed when users select categories in the chart view.Delegates
Developer may optionally implement the
FUIChartSummaryDelegate
, to handle selection of theFUIChartSummaryItem(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:
See morefont-color: Color; font-style: UIFontTextStyle; text-line-clamp: Integer; text-align: NSTextAlignment;
Declaration
Swift
public class FUIChartFloorplanView : FUIDrawingView, FUITitleComponent, FUISubtitleComponent, FUIStatusComponent, FUIAxisTitleComponent, FUISeriesTitleComponent
-
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:
See morefont-color: Color; font-style: UIFontTextStyle; text-line-clamp: Integer; text-align: NSTextAlignment;
Declaration
Swift
@IBDesignable open class FUIChartTitleTableViewCell : FUIBaseDrawingTableViewCell<FUIChartTitleView>
-
Title view component of
FUIChartTitleTableViewCell
,FUIChartFloorplanView
,FUIChartFloorplanViewController
, andFUIChartFloorplanTableViewCell
.Typically not used directly by developer.
Usage
let view = FUIChartTitleView() view.title.text = "Sum of Sales ($) by Month, Quarter, and Year" view.subtitle.text = "2016-2017" view.status.text = "Updated 3 hours ago"
Theming
Supported class paths:
fdlFUIChartTitleView_title {} fdlFUIChartTitleView_subtitle {} fdlFUIChartTitleView_status {}
Supported properties:
See morefont-color: Color; font-style: UIFontTextStyle; text-line-clamp: Integer; text-align: NSTextAlignment;
Declaration
Swift
open class FUIChartTitleView : FUIDrawingView, FUITitleComponent, FUISubtitleComponent, FUIStatusComponent
-
TableViewCell container for Summary content view of Chart Floorplan. Extends API of
FUIChartSummaryView
.Developer should implement
FUIChartSummaryDataSource
to supply summary items, andFUIChartSummaryDelegate
, to handle user selection events.Usage
See morelet cell = tableView.dequeueReusableCell(withIdentifier: FUIChartSummaryTableViewCell.reuseIdentifier, for: indexPath) as! FUIChartSummaryTableViewCell cell.summaryView.seriesTitleText = ["2016", "2017"] cell.summaryView.dataSource = self cell.summaryView.delegate = self return cell
Declaration
Swift
@IBDesignable open class FUIChartSummaryTableViewCell : FUIBaseDrawingTableViewCell<FUIChartSummaryView>
-
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 theFUIChartFloorplan
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 theFUIChartSummaryView
. 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
See moreFUIChartSummaryDelegate
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.Declaration
Swift
open class FUIChartSummaryView : FUIDrawingView, ChartObserver
-
Data source for
FUIChartSummaryView
. Optionally, supply a summary item for the category.Important
theFUIChartSummaryDataSource
will query for a summary item forcategoryIndex = -1
. This item will be displayed at the far left, and should typically contain aggregate info.Declaration
Swift
public protocol FUIChartSummaryDataSource : AnyObject
-
Delegate for
See moreFUIChartSummaryView
. Optionally, supply a selection handler for taps on the category item in the Summary view.Declaration
Swift
public protocol FUIChartSummaryDelegate : AnyObject
-
Item summarizing the category values of series in a chart. Should be supplied to
FUIChartSummaryDataSource
, or, ifFUIChartSummaryView.chartView == nil
, set to theFUIChartSummaryView
usingaddItem(at:)
.Usage
See morelet 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"
Declaration
Swift
public class FUIChartSummaryItem : FUIChartSummaryItemType
-
Undocumented
Declaration
Swift
public class FUIChartSummaryItemType
-
View produced by
FUIChartSummaryView
, for eachFUIChartSummaryItem
supplied by the devloper to theFUIChartSummaryDataSource
.Important
Typically not instantiated by app developer.Declaration
Swift
open class FUIChartSummaryItemView : FUIChartSummaryItemBaseView<FUIChartSummaryItem>, FUITitleComponent, FUIValuesComponent
-
Item summarizing the category values of series in a chart. Should be supplied to
FUIChartSummaryDataSource
, or, ifFUIChartSummaryView.chartView == nil
, set to theFUIChartSummaryView
usingaddItem(at:)
.Usage
See morelet 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"
Declaration
Swift
public class FUIChartRangeSummaryItem : FUIChartSummaryItemType
-
View produced by
FUIChartSummaryView
, for eachFUIChartRangeSummaryItem
supplied by the devloper to theFUIChartSummaryDataSource
.Important
Typically not instantiated by app developer.Declaration
Swift
open class FUIChartRangeSummaryItemView : FUIChartSummaryItemBaseView<FUIChartRangeSummaryItem>
-
Base view of
See moreFUIChartSummaryItem
andFUIChartSummaryRangeItem
views.Declaration
Swift
open class FUIChartSummaryItemBaseView<ItemType> : FUITintableDrawingView<FUIControlState>, FUITrendComponent where ItemType : 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:
See morefont-color: Color; font-style: UIFontTextStyle; text-line-clamp: Integer; text-align: NSTextAlignment;
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:
See morefont-color: Color; font-style: UIFontTextStyle; text-line-clamp: Integer; text-align: NSTextAlignment;
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:
See morefont-color: Color; font-style: UIFontTextStyle; text-line-clamp: Integer; text-align: NSTextAlignment;
Declaration
Swift
@IBDesignable open class FUIChartLegendTableViewCell : FUIBaseDrawingTableViewCell<FUIChartLegendView>
-
Legend view component of
FUIChartLegendTableViewCell
,FUIChartFloorplanView
,FUIChartFloorplanViewController
, andFUIChartFloorplanTableViewCell
.Typically not used directly by developer.
Usage
let view = FUIChartLegendView() view.seriesTitles = ["2016", "2017"] view.seriesColor = [UIColor.preferredFioriColor(forStyle: .chart1), UIColor.preferredFioriColor(forStyle: .chart2)]
Theming
Supported class paths:
fdlFUIChartLegendView_seriesTitles {}
Supported properties:
See morefont-color: Color; font-style: UIFontTextStyle; text-line-clamp: Integer; text-align: NSTextAlignment;
Declaration
Swift
open class FUIChartLegendView : FUIDrawingView, FUISeriesTitleComponent