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.

Series Value Modifier

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.

// Assume a surface has been created and configured somewhere id surface; // Create a modifier let seriesValueModifier = [SCISeriesValueModifier new]; // Add the modifier to the surface [self.surface.chartModifiers add:seriesValueModifier];
// Assume a surface has been created and configured somewhere let surface: ISCIChartSurface // Create a Modifier let seriesValueModifier = SCISeriesValueModifier() // Add the modifier to the surface self.surface.chartModifiers.add(seriesValueModifier)
// Assume a surface has been created and configured somewhere IISCIChartSurface surface; // Create a modifier var seriesValueModifier = new SCISeriesValueModifier(); // Add the modifier to the surface Surface.ChartModifiers.Add(seriesValueModifier);

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:

// Assume renderable series with name “Blue Series” has been created and configured somewhere // Create a factory with a predicate SCIDefaultSeriesValueMarkerFactory *factory = [[SCIDefaultSeriesValueMarkerFactory alloc] initWithPredicate:^BOOL(id series) { return [series.dataSeries.seriesName isEqualToString:@“Blue Series”]; }]; // Create seriesValueModifier with this factory SCISeriesValueModifier *seriesValueModifier = [[SCISeriesValueModifier alloc] initWithMarkerFactory:factory]; // Add the modifier to the surface [self.surface.chartModifiers add:seriesValueModifier];
// Assume renderable series with name “Blue Series” has been created and configured somewhere // Create a factory with a predicate let factory = SCIDefaultSeriesValueMarkerFactory { series -> Bool in guard let series = series as? ISCIRenderableSeries else { return true } return series.dataSeries?.seriesName == “Blue Series” } // Create seriesValueModifier with this factory let seriesValueModifier = SCISeriesValueModifier(markerFactory: factory) // Add the modifier to the surface self.surface.chartModifiers.add(seriesValueModifier)
// Assume renderable series with name “Blue Series” has been created and configured somewhere // Create a factory with a predicate var factory = new SCIDefaultSeriesValueMarkerFactory(obj => ((IISCIRenderableSeries)obj).DataSeries.SeriesName.Equals(“Blue Series”)); // Create seriesValueModifier with this factory var seriesValueModifier = new SCISeriesValueModifier(factory); // Add the modifier to the surface Surface.ChartModifiers.Add(seriesValueModifier);

The result is that only the blue series has a marker:

Series Value Modifier

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 drawing.

NOTE: To learn more about SCIAxisMarkerAnnotation API, follow SCIAxisMarkerAnnotation API article.

As an example of such customization, let’s draw a custom marker without an arrow as a plain rectangle. You will need to write few classes with some implementations to inject the desired marker into the SCISeriesValueModifier pipeline. Those are:

// SCDNoArrowAxisMarkerAnnotation.h #import

@interface SCDNoArrowAxisMarkerAnnotation : SCISeriesValueMarkerAnnotation
@end

// SCDNoArrowAxisMarkerAnnotation.m
#import "SCDNoArrowAxisMarkerAnnotation.h"
#import <SciChart/SCIAxisMarkerAnnotation+Protected.h>

static const CGFloat horizontalPadding = 5;

@implementation SCDNoArrowAxisMarkerAnnotation

- (void)internalDrawMarkerWithContext:(id<ISCIRenderContext2D>)renderContext assetManager:(id<ISCIAssetManager2D>)assetManager inFrame:(CGRect)frame {
    CGRect newFrame = CGRectMake(frame.origin.x - horizontalPadding / 2, frame.origin.y, frame.size.width + horizontalPadding / 2, frame.size.height);
    [super internalDrawMarkerWithContext:renderContext assetManager:assetManager inFrame:newFrame];
}

@end

import SciChart.Protected.SCIAxisMarkerAnnotation

class NoArrowAxisMarkerAnnotation: SCISeriesValueMarkerAnnotation {
    private let horizontalPadding: CGFloat = 5

    override func internalDrawMarker(with renderContext: ISCIRenderContext2D!, assetManager: ISCIAssetManager2D!, inFrame frame: CGRect) {
        let newFrame = CGRect(x: frame.origin.x - horizontalPadding / 2, y: frame.origin.y, width: frame.width + horizontalPadding / 2, height: frame.height)
        super.internalDrawMarker(with: renderContext, assetManager: assetManager, inFrame: newFrame)
    }
}

class NoArrowAxisMarkerAnnotation : SCISeriesValueMarkerAnnotation { private const float horizontalPadding = 5;

    protected override void InternalDrawMarker(IISCIRenderContext2D renderContext, IISCIAssetManager2D assetManager, CGRect frame)
    {
        CGRect newFrame = new CGRect(frame.X - horizontalPadding / 2, frame.Y, frame.Width + horizontalPadding / 2, frame.Height);
        base.InternalDrawMarker(renderContext, assetManager, newFrame);
    }
}

  • Subclass SCIDefaultSeriesValueMarker, override protected method -[SCIDefaultSeriesValueMarker createAxisMarkerAnnotation] and return your custom marker annotation:

// SCDNoArrowSeriesValueMarker.h #import

@interface SCDNoArrowSeriesValueMarker : SCIDefaultSeriesValueMarker
@end

// SCDNoArrowSeriesValueMarker.m
#import "SCDNoArrowSeriesValueMarker.h"
#import <SciChart/SCIDefaultSeriesValueMarker+Protected.h>

@implementation SCDNoArrowSeriesValueMarker

- (SCISeriesValueMarkerAnnotation *)createAxisMarkerAnnotation {
    SCISeriesValueMarkerAnnotation *marker = [[SCISeriesValueMarkerAnnotation alloc] initWithRenderableSeries:self.renderableSeries predicate:self.isValidRenderableSeriesPredicate];
    marker.markerPointSize = 0;

    return marker;
}

import SciChart.Protected.SCIDefaultSeriesValueMarker

class NoArrowSeriesValueMarker: SCIDefaultSeriesValueMarker {

    override func createAxisMarkerAnnotation() -> SCISeriesValueMarkerAnnotation {
        let marker = SCISeriesValueMarkerAnnotation(renderableSeries: renderableSeries, predicate: isValidRenderableSeriesPredicate)
        marker.markerPointSize = 0

        return marker
    }
}

class NoArrowSeriesValueMarker : SCIDefaultSeriesValueMarker { public NoArrowSeriesValueMarker(IISCIRenderableSeries renderableSeries) : base(renderableSeries, (NSObject arg0) => true) { }

     protected override SCIAxisMarkerAnnotation CreateAxisMarkerAnnotation()
     {
         var marker = new SCISeriesValueMarkerAnnotation(this.RenderableSeries, this.IsValidRenderableSeriesPredicate);
         marker.MarkerPointSize = 0;

         return marker;
     }
 }

// SCDNoArrowSeriesValueMarkerFactory.h #import

@interface SCDNoArrowSeriesValueMarkerFactory : NSObject<ISCISeriesValueMarkerFactory>
@end

// SCDNoArrowSeriesValueMarkerFactory.m
#import "SCDNoArrowSeriesValueMarkerFactory.h"
#import "SCDNoArrowSeriesValueMarker.h"

@implementation SCDNoArrowSeriesValueMarkerFactory

@synthesize projectionFunction = _projectionFunction;

- (instancetype)init {
    self = [super init];
    if (self) {
        _projectionFunction = ^id<ISCISeriesValueMarker>(id<ISCIRenderableSeries> renderableSeries) {
            return [[SCDNoArrowSeriesValueMarker alloc] initWithRenderableSeries:renderableSeries predicate:^BOOL(id item) {
                return YES;
            }];
        };
    }
    return self;
}

class NoArrowSeriesValueMarkerFactory: NSObject, ISCISeriesValueMarkerFactory { var projectionFunction: CreateMarkerFunc

    override init() {
        projectionFunction = { series in
            return NoArrowSeriesValueMarker(renderableSeries: series) { _ in return true }
        }
    }
}

class NoArrowSeriesValueMarkerFactory : NSObject, IISCISeriesValueMarkerFactory { public CreateMarkerFunc ProjectionFunction => (IISCIRenderableSeries series) => { return new NoArrowSeriesValueMarker(series); }; }

SCISeriesValueModifier *seriesValueModifier = [[SCISeriesValueModifier alloc] initWithMarkerFactory:[SCDNoArrowSeriesValueMarkerFactory new]]; [self.surface.chartModifiers add:seriesValueModifier];
let seriesValueModifier = SCISeriesValueModifier(markerFactory: NoArrowSeriesValueMarkerFactory()) self.surface.chartModifiers.add(seriesValueModifier)
var seriesValueModifier = new SCISeriesValueModifier(new NoArrowSeriesValueMarkerFactory()); Surface.ChartModifiers.Add(seriesValueModifier);

This produces the following output:

Series Value Modifier Custom Marker