iOS & macOS Charting Documentation - SciChart iOS & macOS Charts SDK v4.x
SCISeriesValueModifier
The SCISeriesValueModifier is a custom ChartModifier that places a SCISeriesValueMarkerAnnotation on the YAxis for each RenderableSeries in the chart, showing the current ISCIRenderableSeries latest Y-value.
NOTE: Examples of the
SCISeriesValueModifierusage can be found in the SciChart iOS Examples Suite as well as on GitHub:
Adding the SCISeriesValueModifier to a Chart
Any Chart Modifier can be added to a SCIChartSurface via the ISCIChartSurface.chartModifiers property and SCISeriesValueModifier is no different.
NOTE: To learn more about features available, Chart Modifier APIs article.
Excluding a Series from the SeriesValueModifier
By default, all RenderableSeries from ISCIChartSurface.renderableSeries collection are included in the SCISeriesValueModifier and thus will have an axis marker placed on YAxis. This behavior can be controlled and to do so you’ll need to create a SCIDefaultSeriesValueMarkerFactory instance with a predicate to decide which series would you like to include or exclude.
Imagine you have three ISCIRenderableSeries and you want to show marker only for a series with the name “Blue Series”. Please see the code below, which shows how to do just that:
The result is that only the blue series has a marker:

Styling the SCISeriesValueModifier Axis Markers
By default, SCISeriesValueModifier uses SCIDefaultSeriesValueMarker to create SCISeriesValueMarkerAnnotation. You can customize a marker annotation style or provide some complete custom annotation.
NOTE: To learn more about
SCIAxisMarkerAnnotationAPI, follow SCIAxisMarkerAnnotation API article.
As an example of such customization, let’s place a SCIHorizontalLineAnnotation with an axis label. You will need to write few classes with some implementations to inject the desired marker into the SCISeriesValueModifier pipeline. Those are:
- Subclass
SCIHorizontalLineAnnotationwithSCISeriesValueMarkerAnnotationHelperas a initializer parameter to call-[SCIDefaultSeriesValueMarkerAnnotationHelper tryUpdateAnnotation:]method when needed:
@interface SCDHorizontalLineSeriesValueMarkerAnnotation : SCIHorizontalLineAnnotation
- (instancetype)initWithSeriesValueHelper:(SCIDefaultSeriesValueMarkerAnnotationHelper *)seriesValueHelper;
@end
@implementation SCDHorizontalLineSeriesValueMarkerAnnotation {
SCIDefaultSeriesValueMarkerAnnotationHelper *_seriesValueHelper;
}
- (instancetype)initWithSeriesValueHelper:(SCIDefaultSeriesValueMarkerAnnotationHelper<SCDHorizontalLineSeriesValueMarkerAnnotation *> *)seriesValueHelper {
self = [super init];
if (self) {
_seriesValueHelper = seriesValueHelper;
}
return self;
}
- (void)attachTo:(id<ISCIServiceContainer>)services {
id<ISCIRenderableSeries> renderableSeries = _seriesValueHelper.renderableSeries;
self.xAxisId = renderableSeries.xAxisId;
self.yAxisId = renderableSeries.yAxisId;
[super attachTo:services];
}
- (void)updateWithXAxis:(id<ISCIAxis>)xAxis yAxis:(id<ISCIAxis>)yAxis {
[_seriesValueHelper tryUpdateAnnotation:self];
[super updateWithXAxis:xAxis yAxis:yAxis];
}
@end
class HorizontalLineSeriesValueMarkerAnnotation: SCIHorizontalLineAnnotation {
private let seriesValueHelper: SCIDefaultSeriesValueMarkerAnnotationHelper<HorizontalLineSeriesValueMarkerAnnotation>
init(seriesValueHelper: SCIDefaultSeriesValueMarkerAnnotationHelper<HorizontalLineSeriesValueMarkerAnnotation>) {
self.seriesValueHelper = seriesValueHelper
}
override func attach(to services: ISCIServiceContainer) {
let renderableSeries = seriesValueHelper.renderableSeries
xAxisId = renderableSeries.xAxisId
yAxisId = renderableSeries.yAxisId
super.attach(to:services)
}
override func update(withXAxis xAxis: ISCIAxis, yAxis: ISCIAxis) {
seriesValueHelper.tryUpdateAnnotation(self)
super.update(withXAxis: xAxis, yAxis: yAxis)
}
}
- Subclass
SCISeriesValueMarkerAnnotationHelperwhere we will style our annotation and annotation labels:
#import <SciChart/SCIDefaultSeriesValueMarkerAnnotationHelper+Protected.h>
@interface SCDHorizontalLineSeriesValueMarkerAnnotationHelper : SCIDefaultSeriesValueMarkerAnnotationHelper<SCDHorizontalLineSeriesValueMarkerAnnotation *>
@end
@implementation SCDHorizontalLineSeriesValueMarkerAnnotationHelper {
float _lineThickness;
NSArray<NSNumber *> *_dashPattern;
}
- (instancetype)initWithRenderableSeries:(id<ISCIRenderableSeries>)renderableSeries predicate:(SCIPredicate)isValidRenderableSeriesPredicate {
self = [super initWithRenderableSeries:renderableSeries predicate:isValidRenderableSeriesPredicate];
if (self) {
_lineThickness = 1;
_dashPattern = [NSArray arrayWithObjects:@4, @4, nil];
}
return self;
}
- (void)updateAnnotation:(SCDHorizontalLineSeriesValueMarkerAnnotation *)annotation lastValue:(id<ISCIComparable>)lastValue lastColor:(UIColor *)lastColor {
[super updateAnnotation:annotation lastValue:lastValue lastColor:lastColor];
annotation.stroke = [[SCISolidPenStyle alloc] initWithColor:lastColor thickness:_lineThickness strokeDashArray:_dashPattern];
NSInteger count = annotation.annotationLabels.count;
for (NSInteger i = 0; i <count; i++) {
SCIAnnotationLabel *label = annotation.annotationLabels[i];
label.backgroundBrush = [[SCISolidBrushStyle alloc] initWithColor:lastColor];
label.fontStyle = [[SCIFontStyle alloc] initWithFontSize:12.0 andTextColorCode:[UIColor getInvertedColor:[lastColor colorARGBCode]]];
}
}
@end
import SciChart.Protected.SCIDefaultSeriesValueMarkerAnnotationHelper
class HorizontalLineSeriesValueMarkerAnnotationHelper: SCIDefaultSeriesValueMarkerAnnotationHelper<HorizontalLineSeriesValueMarkerAnnotation> {
private let lineThickness: Float = 1
private let dashPattern: [NSNumber] = [4, 4]
override func updateAnnotation(annotation: HorizontalLineSeriesValueMarkerAnnotation, lastValue: ISCIComparable, lastColor: UIColor) {
super.updateAnnotation(annotation: annotation, lastValue: lastValue, lastColor: lastColor)
annotation.stroke = SCISolidPenStyle(color: lastColor, thickness: lineThickness, strokeDashArray: dashPattern)
for i in 0..<annotation.annotationLabels.count {
let label = annotation.annotationLabels[i]
label.backgroundBrush = SCISolidBrushStyle(color: lastColor)
label.fontStyle = SCIFontStyle(fontSize: 12, andTextColorCode: UIColor.getInvertedColor(lastColor.colorARGBCode()))
}
}
}
- Subclass
SCISeriesValueMarkerBase, where we will create, destroy, add or remove our custom markerAnnotation when needed:
#import <SciChart/SCISeriesValueMarkerBase+Protected.h>
@interface SCDHorizontalLineSeriesValueMarker : SCISeriesValueMarkerBase
@end
@implementation SCDHorizontalLineSeriesValueMarker {
SCDHorizontalLineSeriesValueMarkerAnnotation *_markerAnnotation;
}
- (void)tryRemoveMarkerAnnotation:(id<ISCIChartSurface>)parentSurface {
if (_markerAnnotation != nil) {
[parentSurface.annotations remove:_markerAnnotation];
}
}
- (void)tryAddMarkerAnnotation:(id<ISCIChartSurface>)parentSurface {
if (_markerAnnotation != nil) {
[parentSurface.annotations safeAdd:_markerAnnotation];
}
}
- (void)createMarkerAnnotation {
_markerAnnotation = [[SCDHorizontalLineSeriesValueMarkerAnnotation alloc] initWithSeriesValueHelper:[[SCDHorizontalLineSeriesValueMarkerAnnotationHelper alloc] initWithRenderableSeries:self.renderableSeries predicate:self.isValidRenderableSeriesPredicate]];
SCIAnnotationLabel *label = [SCIAnnotationLabel new];
label.labelPlacement = SCILabelPlacement_Axis;
[_markerAnnotation.annotationLabels add:label];
}
- (void)destroyMarkerAnnotation {
_markerAnnotation = nil;
}
@end
import SciChart.Protected.SCISeriesValueMarkerBase
class HorizontalLineSeriesValueMarker: SCISeriesValueMarkerBase {
private var markerAnnotation: HorizontalLineSeriesValueMarkerAnnotation?
override func tryRemoveMarkerAnnotation(_ parentSurface: ISCIChartSurface) {
if let markerAnnotation = markerAnnotation {
parentSurface.annotations.remove(markerAnnotation)
}
}
override func tryAddMarkerAnnotation(_ parentSurface: ISCIChartSurface) {
if let markerAnnotation = markerAnnotation {
parentSurface.annotations.safeAdd(markerAnnotation)
}
}
override func createMarkerAnnotation() {
markerAnnotation = HorizontalLineSeriesValueMarkerAnnotation(
seriesValueHelper: HorizontalLineSeriesValueMarkerAnnotationHelper(
renderableSeries: renderableSeries,
predicate: isValidRenderableSeriesPredicate
)
)
let label = SCIAnnotationLabel()
label.labelPlacement = .axis
markerAnnotation?.annotationLabels.add(label)
}
override func destroyMarkerAnnotation() {
markerAnnotation = nil
}
}
- Create a custom
ISCISeriesValueMarkerFactoryand implementCreateMarkerFuncprojectionFunction which will return our custom marker:
- Create a
SCISeriesValueModifierwith your custom factory and add it to the surface:
This produces the following output:

View on GitHub