SciChart iOS v2.x API > Axis APIs > Axis Labels - LabelProvider API
Axis Labels - LabelProvider API

Labelprovider API - Full Control over Axis Labels

All Axis Types include the AxisCore.LabelProvider property, which allows a class to be attached to an axis for complete control over axis label output.

Use the LabelProvider when you want to:

  • Have fine grained control over Axis Text or Cursor Labels, depending on numeric (or date) values
  • Display strings on the XAxis, e.g. “Bananas”, “Oranges”, “Apples” not “1”, “2”, “3”
  • Dynamically change the Axis TextFormatting as you zoom in or out
  • Dynamically change the Axis TextFormatting depending on Data-value

Creating your own LabelProviders

By default each axis as a LabelProvider created and assigned to the AxisCore.LabelProvider property. The type of LabelProvider depends on the type of the axis. Below is a table of the LabelProviders already defined in SciChart iOS.

If you create a LabelProvider, inherit from the correct class above, and override formatLabel and formatCursorLabel methods.

 

 Worked Example: Creating a Custom NumericAxis LabelProvider

To create a custom label provider for the SCINumericAxis, we simply create a class that inherits SCINumericLabelProvider, and override formatLabel() and optionally, formatCursorLabel().

Other axis types require different base types. Please read the comments in the code sample below for additional information.

 

// BillionsLabelProvider.h - Formats labels as B symbol when larger than 1x10E9
#import <SciChart/SciChart.h>
@interface BillionsLabelProvider : SCINumericLabelProvider
@end

// BillionsLabelProvider.m - Implementation of BillionsLabelProvider
#import "BillionsLabelProvider.h"
@implementation BillionsLabelProvider
- (NSString *)formatLabel:(SCIGenericType)dataValue {
    return [NSString stringWithFormat: @"%@B", [super formatLabel:SCIGeneric(SCIGenericDouble(dataValue) / pow(10, 9))]];
}
@end


// ThousandsLabelProvider.h
// Formats labels as K when larger than 1x10E3, e.g. 2,000 becomes 2k
#import <SciChart/SciChart.h>
@interface ThousandsLabelProvider : SCINumericLabelProvider
@end

// ThousandsLabelProvider.m
// Formats labels as K when larger than 1x10E3, e.g. 2,000 becomes 2k
#import "ThousandsLabelProvider.h"
@implementation ThousandsLabelProvider
-(NSString*)formatLabel:(SCIGenericType)dataValue{
    return [NSString stringWithFormat:@"%.1fk", SCIGenericDouble(dataValue)/1000];
}
@end

// Usage in example - application of custom labelprovider to an axis
SCINumericAxis * xAxis = [[SCINumericAxis alloc] init];
SCINumericAxis * yAxis = [[SCINumericAxis alloc] init];
xAxis.labelProvider = [[ThousandsLabelProvider alloc] init];
yAxis.labelProvider = [[BillionsLabelProvider alloc] init];
import SciChart

// Formats labels as K when larger than 1x10E3, e.g. 2,000 becomes 2k
class ThousandsLabelProvider: SCINumericLabelProvider {
    override func formatLabel(_ dataValue: SCIGenericType) -> Swift.String! {
        return super.formatLabel(SCIGeneric(SCIGenericDouble(dataValue) / 1000)) + "k"
    }
}

// Formats labels as B symbol when larger than 1x10E9, e.g. 2,000,000 becomes 2B
class BillionsLabelProvider: SCINumericLabelProvider {
    override func formatLabel(_ dataValue: SCIGenericType) -> Swift.String! {
        return super.formatLabel(SCIGeneric(SCIGenericDouble(dataValue) / pow(10, 9))) + "B"
    }
}

// Usage in example - application of custom labelprovider to an axis
var xAxis = SCINumericAxis()
var yAxis = SCINumericAxis()
xAxis.labelProvider = ThousandsLabelProvider()
yAxis.labelProvider = BillionsLabelProvider()
using SciChart.iOS.Charting;

// Formats labels as K when larger than 1x10E3, e.g. 2,000 becomes 2k
public class ThousandsLabelProvider : SCINumericLabelProvider
{
    public override string FormatLabel(SCIGenericType dataValue)
    {
        return base.FormatLabel(new SCIGenericType(dataValue.doubleData / 1000)) + "k";
    }
}

// Formats labels as B symbol when larger than 1x10E9, e.g. 2,000,000 becomes 2B
public class BillionsLabelProvider : SCINumericLabelProvider
{
    public override string FormatLabel(SCIGenericType dataValue)
    {
        return base.FormatLabel(new SCIGenericType(dataValue.doubleData / 1E9)) + "B";
    }
}

// Usage in example - application of custom labelprovider to an axis
var xAxis = new SCINumericAxis();
var yAxis = new SCINumericAxis();
xAxis.labelProvider = new ThousandsLabelProvider();
yAxis.labelProvider = new BillionsLabelProvider();
NOTE: For info on usage of the SCIGenericType macro please see the article SCIGenericType, SCIGenericDouble and SCIGenericDate.

 

 Worked Example: Creating a Custom DateTimeAxis LabelProvider

To create a custom label provider for the SCIDateTimeAxis, you don't have to inherit SCIDateTimeLabelProvider, you can simply instead inherit SCILabelProviderBase and override formatLabel() and optionally, formatCursorLabel() to provide custom formatting for your axis.

Here is our implementation for SCIDateTimeLabelProvider which provides a different date format on the axis depending on the current zoom level of this axis. Feel free to modify it for your needs.

// CustomLabelProvider for a DateTimeAxis
// Demonstrates how to dynamically change the TextFormatting based on the
// current VisibleRange of the attached axis, e.g. when zoomed in, display HH:mm:ss
// and zoomed out, display dd/mm/yyyy
@interface CustomLabelProvider : SCIDateTimeLabelProvider
@end

@implementation CustomLabelProvider {
    NSDateFormatter * _dateFormatLessThanDay;
    NSDateFormatter * _dateFormatMoreThanDay;
}

-(instancetype)init {
    self = [super init];
    if (self) {
        _dateFormatLessThanDay = [[NSDateFormatter alloc] init];
        _dateFormatLessThanDay.dateFormat = @"hh:mm:ss";
       
        _dateFormatMoreThanDay = [[NSDateFormatter alloc] init];
        _dateFormatMoreThanDay.dateFormat = @"dd/mm/yyyy";
    }
    return self;
}

-(NSString *)formatLabel:(SCIGenericType)dataValue {
    const double SECONDS_PER_DAY = 24  60  60;

    // using the parent axis VisibleRange diff (max-min) we determine the time range 
    // on the axis in seconds
    double axisTimeRangeInSeconds =  SCIGenericDouble( [self.parentAxis.visibleRange diff] );

    // If less than one day visible, display a DateFormat for less than one day
    if (axisTimeRangeInSeconds < SECONDS_PER_DAY) {
        return [_dateFormatLessThanDay stringFromDate: SCIGenericDate(dataValue)];
    // Otherwise, display a date format for greater than one day
    } else {
        return [_dateFormatMoreThanDay stringFromDate: SCIGenericDate(dataValue)];
    }
}
@end

//
// Usage of CustomLabelProvider
//
SCIDateTimeAxis * xAxis = [[SCIDateTimeAxis alloc] init];
xAxis.labelProvider = [[SCICustomDateTimeLabelProvider alloc] init];
// CustomLabelProvider for a DateTimeAxis
// Demonstrates how to dynamically change the TextFormatting based on the
// current VisibleRange of the attached axis, e.g. when zoomed in, display HH:mm:ss
// and zoomed out, display dd/mm/yyyy
class CustomLabelProvider: SCIDateTimeLabelProvider {
    private var dateFormatLessThanDay: DateFormatter?
    private var dateFormatMoreThanDay: DateFormatter?
    init() {
        super.init()
       
        dateFormatLessThanDay = DateFormatter()
        dateFormatLessThanDay?.dateFormat = "hh:mm:ss"
        dateFormatMoreThanDay = DateFormatter()
        dateFormatMoreThanDay?.dateFormat = "dd/mm/yyyy"    
    }

    func formatLabel(_ dataValue: SCIGenericType) -> String {
        let SECONDS_PER_DAY: Double = 86400
        
        // using the parent axis VisibleRange diff (max-min) we determine the time range
        // on the axis in seconds
        let axisTimeRangeInSeconds = SCIGenericDouble(parentAxis.visibleRange.diff())
       
        // If less than one day visible, display a DateFormat for less than one day
        if axisTimeRangeInSeconds < SECONDS_PER_DAY {
            return dateFormatLessThanDay?.string(from: SCIGenericDate(dataValue)) ?? ""
        }
        // Otherwise, display a date format for greater than one day
        else {
            return dateFormatMoreThanDay?.string(from: SCIGenericDate(dataValue)) ?? ""
        }
    }
}

//
// Usage of CustomLabelProvider
//
var xAxis = SCIDateTimeAxis()
xAxis.labelProvider = CustomLabelProvider()
// CustomLabelProvider for a DateTimeAxis
// Demonstrates how to dynamically change the TextFormatting based on the
// current VisibleRange of the attached axis, e.g. when zoomed in, display HH:mm:ss
// and zoomed out, display dd/mm/yyyy

public class CustomLabelProvider : SCIDateTimeLabelProvider
{
    private string dateFormatLessThanDay = "hh:mm:ss";
    private string dateFormatMoreThanDay = "dd-mm-yyyy";
    public override string FormatLabel(SCIGenericType dataValue)
    {
        ISCIAxisCoreProtocol parentAxis = base.ParentAxis;
        ISCIRangeProtocol axisCurrentRange = parentAxis.VisibleRange;
        DateTime max = axisCurrentRange.Max_native.dateTimeData;
        DateTime min = axisCurrentRange.Min_native.dateTimeData;
        TimeSpan axisRangeDifference = max - min;
        DateTime theDateToFormat = dataValue.dateTimeData;
        if (axisRangeDifference > TimeSpan.FromDays(1))
        {
            return theDateToFormat.ToString(dateFormatMoreThanDay);
        }
        else
        {
            return theDateToFormat.ToString(dateFormatLessThanDay);
        }               
    }
    public override string FormatCursorLabel(SCIGenericType dataValue)
    {
        return FormatLabel(dataValue);
    }            
}

//
// Usage of CustomLabelProvider
//
var xAxis = new SCIDateTimeAxis();
xAxis.labelProvider = new CustomLabelProvider();
NOTE: For info on usage of the SCIGenericType macro please see the article SCIGenericType, SCIGenericDouble and SCIGenericDate.

 

 Worked Example: Creating a Custom CategoryDateTimeAxis LabelProvider

To create a custom label provider for the SCICategoryDateTimeAxis, we simply create a class that inherits SCICategoryDateTimeLabelProvider, and override formatLabel() and optionally, formatCursorLabel().

This type of label provider works differently as the VisibleRange of the axis is an index range, whereas the axis labels are displaying dates. As a result, we have to do some conversion between the indices and dates to perform string formatting.

 

// CustomLabelProvider for a CategoryDateTimeAxis
// Demonstrates how to dynamically change the TextFormatting based on the
// current VisibleRange of the attached axis, e.g. when zoomed in, display HH:mm:ss
// and zoomed out, display dd/mm/yyyy
@interface CustomCategoryLabelProvider : SCICategoryDateTimeLabelProvider
@end
@implementation CustomCategoryLabelProvider {
    NSDateFormatter * _dateFormatLessThanDay;
    NSDateFormatter * _dateFormatMoreThanDay;
}
-(instancetype)init {
    self = [super init];
    if (self) {
        _dateFormatLessThanDay = [[NSDateFormatter alloc] init];
        _dateFormatLessThanDay.dateFormat = @"hh:mm:ss";
       
        _dateFormatMoreThanDay = [[NSDateFormatter alloc] init];
        _dateFormatMoreThanDay.dateFormat = @"dd/mm/yyyy";
    }
    return self;
}
-(NSString *)formatLabel:(SCIGenericType)dataValue {
    const double SECONDS_PER_DAY = 24 * 60 * 60;

    // Get the SCICategoryDateTimeAxis instance
    SCICategoryDateTimeAxis * dtAxis = (SCICategoryDateTimeAxis*)self.parentAxis;

    // Get the axis controller which has the XAxis data on it as dates
    id<SCIRangeProtocol> dataRange = [dtAxis getDataRange];
    
    // Now convert between index and dates
    double minDateTime = SCIGenericDouble(dataRange.min);
    double maxDateTime = SCIGenericDouble(dataRange.max);
   
    // Finally determine the axis range in seconds (seconds between min and max date)
    double axisTimeRangeInSeconds = fabs(minDateTime - maxDateTime);
    // If less than one day visible, display a DateFormat for less than one day
    if (axisTimeRangeInSeconds < SECONDS_PER_DAY) {
        return [_dateFormatLessThanDay stringFromDate: SCIGenericDate(dataValue)];
    // Otherwise, display a date format for greater than one day
    } else {
        return [_dateFormatMoreThanDay stringFromDate: SCIGenericDate(dataValue)];
    }
}
@end

//
// Usage of CustomLabelProvider
//
SCIDateTimeAxis * xAxis = [[SCIDateTimeAxis alloc] init];
xAxis.labelProvider = [[SCICustomDateTimeLabelProvider alloc] init];
// CustomLabelProvider for a CategoryDateTimeAxis
// Demonstrates how to dynamically change the TextFormatting based on the
// current VisibleRange of the attached axis, e.g. when zoomed in, display HH:mm:ss
// and zoomed out, display dd/mm/yyyy
class CustomCategoryLabelProvider: SCICategoryDateTimeLabelProvider {
    private var dateFormatLessThanDay: DateFormatter?
    private var dateFormatMoreThanDay: DateFormatter?
    init() {
        super.init()
       
        dateFormatLessThanDay = DateFormatter()
        dateFormatLessThanDay?.dateFormat = "hh:mm:ss"
        dateFormatMoreThanDay = DateFormatter()
        dateFormatMoreThanDay?.dateFormat = "dd/mm/yyyy"
   
    }
    func formatLabel(_ dataValue: SCIGenericType) -> String {
        let SECONDS_PER_DAY: Double = 24 * 60 * 60

        // Get the SCICategoryDateTimeAxis instance
        let dtAxis = parentAxis as? SCICategoryDateTimeAxis

        let dataRange = dtAxis?.getDataRange();
       
        let minDateTime = SCIGenericDouble(dataRange?.min)
        let maxDateTime = SCIGenericDouble(dataRange?.max)
       
        // Finally determine the axis range in seconds (seconds between min and max date)
        let axisTimeRangeInSeconds: Double = fabs(minDateTime - maxDateTime)

        // If less than one day visible, display a DateFormat for less than one day
        if axisTimeRangeInSeconds < SECONDS_PER_DAY {
            return dateFormatLessThanDay?.string(from: SCIGenericDate(dataValue)) ?? ""            
        }
        // Otherwise, display a date format for greater than one day
        else {
            return dateFormatMoreThanDay?.string(from: SCIGenericDate(dataValue)) ?? ""
        }
    }
}


//
// Usage of CustomLabelProvider
// 
let xAxis = SCICategoryDateTimeAxis()
xAxis.labelProvider = CustomCategoryLabelProvider()
public class CustomLabelProvider : SCIDateTimeLabelProvider
{
    private string dateFormatLessThanDay = "hh:mm:ss";
    private string dateFormatMoreThanDay = "dd-mm-yyyy";
    public override string FormatLabel(SCIGenericType dataValue)
     {
         // Get the SCICategoryDateTimeAxis instance
         var dtAxis = base.ParentAxis as SCICategoryDateTimeAxis;
         var dataRange = dtAxis.GetDataRange().AsDoubleRange();
         var minDateTime = dataRange.Min;
         var maxDateTime = dataRange.Max;
         // Finally determine the axis range in seconds (seconds between min and max date)
         var axisTimeRangeInSeconds = Math.Abs(minDateTime - maxDateTime);
         DateTime theDateToFormat = dataValue.dateTimeData;
         // If less than one day visible, display a DateFormat for less than one day
         if (axisTimeRangeInSeconds > TimeSpan.FromDays(1).TotalSeconds)
         {
             return theDateToFormat.ToString(dateFormatMoreThanDay);
         }
         return theDateToFormat.ToString(dateFormatLessThanDay);
     }
     public override string FormatCursorLabel(SCIGenericType dataValue)
     {
         return FormatLabel(dataValue);
     }
}
NOTE: For info on usage of the SCIGenericType macro please see the article SCIGenericType, SCIGenericDouble and SCIGenericDate.

 

See Also