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
SCISeriesValueModifier
usage 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
SCIAxisMarkerAnnotation
API, 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
SCIHorizontalLineAnnotation
withSCISeriesValueMarkerAnnotationHelper
as 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
SCISeriesValueMarkerAnnotationHelper
where 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
ISCISeriesValueMarkerFactory
and implementCreateMarkerFunc
projectionFunction which will return our custom marker:
- Create a
SCISeriesValueModifier
with your custom factory and add it to the surface:
This produces the following output: