Pre loader

iOS & macOS FIFO Scrolling Chart

iOS & macOS charts - Examples

Please note! These examples are new to SciChart iOS v4 release! SciChart’s OpenGL ES and Metal iOS and Metal macOS Chart library ships with hundred of Objective-C and Swift iOS & macOS Chart Examples which you can browse, play with and view the source-code. All of this is possible with the new and improved SciChart iOS Examples Suite and demo application for Mac, which ships as part of the SciChart SDK.

Download Examples

Demonstrates the difference between FIFO and standard DataSeries. A FIFO series discards old data-points once a predefined number of points is met. This type of data-series is extremely efficient for scrolling charts, where you do not care about discarded (off-screen) data.

FIFO Series can be used in more ways than just scrolling a chart. For instance, reload the entire FIFO buffer each frame to get a Spectrum Analyzer style chart. Or, use FIFO series and set the XAxis.VisibleRange to achieve an ECG style chart.

The Swift and Objective-C source code for the iOS and macOS FIFO Scrolling Chart example is included below (Scroll down!).

Did you know that we have the source code for all our example available for free on Github?

Clone the SciChart.iOS.Examples from Github.

Also the SciChart iOS and Scichart macOS Trials contain the full source for the examples (link below).

DOWNLOAD THE IOS CHART EXAMPLES

FIFOSpeedTestSciChart.m
View source code
//******************************************************************************
// SCICHART® Copyright SciChart Ltd. 2011-2019. All rights reserved.
//
// Web: http://www.scichart.com
// Support: support@scichart.com
// Sales:   sales@scichart.com
//
// FifoScrollingChartView.m is part of the SCICHART® Examples. Permission is hereby granted
// to modify, create derivative works, distribute and publish any part of this source
// code whether for commercial, private or personal use.
//
// The SCICHART® examples are distributed in the hope that they will be useful, but
// without any warranty. It is provided "AS IS" without warranty of any kind, either
// expressed or implied.
//******************************************************************************

#import "FifoScrollingChartView.h"
#import "SCDToolbarItem.h"
#import "SCDRandomUtil.h"
#import "SCDToolbarButtonsGroup.h"

static int const FifoCapacicty = 50;
static double const TimeInterval = 30.0;
static double const OneOverTimeInterval = 1.0 / TimeInterval;
static double const VisibleRangeMax = FifoCapacicty * OneOverTimeInterval;
static double const GrowBy = VisibleRangeMax * 0.1;

@implementation FifoScrollingChartView {
    NSTimer *_timer;
    SCIXyDataSeries *_ds1;
    SCIXyDataSeries *_ds2;
    SCIXyDataSeries *_ds3;
    
    double _t;
    BOOL _isRunning;
}

- (Class)associatedType { return SCIChartSurface.class; }

- (NSArray<id<ISCDToolbarItem>> *)provideExampleSpecificToolbarItems {
    __weak typeof(self) wSelf = self;

    return @[[[SCDToolbarButtonsGroup alloc] initWithToolbarItems:@[
        [[SCDToolbarItem alloc] initWithTitle:@"Start" image:[SCIImage imageNamed:@"chart.play"] andAction:^{ self->_isRunning = YES; }],
        [[SCDToolbarItem alloc] initWithTitle:@"Pause" image:[SCIImage imageNamed:@"chart.pause"] andAction:^{ self->_isRunning = NO; }],
        [[SCDToolbarItem alloc] initWithTitle:@"Stop" image:[SCIImage imageNamed:@"chart.stop"] andAction:^{
            self->_isRunning = NO;
            [wSelf resetChart];
        }],
    ]]];
}

- (void)initExample {
    id<ISCIAxis> xAxis = [SCINumericAxis new];
    xAxis.autoRange = SCIAutoRange_Never;
    xAxis.visibleRange = [[SCIDoubleRange alloc] initWithMin:-GrowBy max:VisibleRangeMax + GrowBy];
    
    id<ISCIAxis> yAxis = [SCINumericAxis new];
    yAxis.autoRange = SCIAutoRange_Always;
    
    _ds1 = [[SCIXyDataSeries alloc] initWithXType:SCIDataType_Double yType:SCIDataType_Double];
    _ds1.fifoCapacity = FifoCapacicty;
    _ds2 = [[SCIXyDataSeries alloc] initWithXType:SCIDataType_Double yType:SCIDataType_Double];
    _ds2.fifoCapacity = FifoCapacicty;
    _ds3 = [[SCIXyDataSeries alloc] initWithXType:SCIDataType_Double yType:SCIDataType_Double];
    _ds3.fifoCapacity = FifoCapacicty;

    SCIFastLineRenderableSeries *rSeries1 = [SCIFastLineRenderableSeries new];
    rSeries1.dataSeries = _ds1;
    rSeries1.strokeStyle = [[SCISolidPenStyle alloc] initWithColorCode:0xFFe97064 thickness:2];
    
    SCIFastLineRenderableSeries *rSeries2 = [SCIFastLineRenderableSeries new];
    rSeries2.dataSeries = _ds2;
    rSeries2.strokeStyle = [[SCISolidPenStyle alloc] initWithColorCode:0xFF47bde6 thickness:2];
    
    SCIFastLineRenderableSeries *rSeries3 = [SCIFastLineRenderableSeries new];
    rSeries3.dataSeries = _ds3;
    rSeries3.strokeStyle = [[SCISolidPenStyle alloc] initWithColorCode:0xFFae418d thickness:2];
    
    [SCIUpdateSuspender usingWithSuspendable:self.surface withBlock:^{
        [self.surface.xAxes add:xAxis];
        [self.surface.yAxes add:yAxis];
        [self.surface.renderableSeries add:rSeries1];
        [self.surface.renderableSeries add:rSeries2];
        [self.surface.renderableSeries add:rSeries3];
    }];
    
    _timer = [NSTimer scheduledTimerWithTimeInterval:TimeInterval / 1000.0 target:self selector:@selector(updateData:) userInfo:nil repeats:YES];
#if TARGET_OS_OSX
    if (_timer) {
        [[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
    }
#endif
    _isRunning = YES;
}

- (void)updateData:(NSTimer *)timer {
    if (!_isRunning) return;
    
    double y1 = 3.0 * sin(((2 * M_PI) * 1.4) * _t) + [SCDRandomUtil nextDouble] * 0.5;
    double y2 = 2.0 * cos(((2 * M_PI) * 0.8) * _t) + [SCDRandomUtil nextDouble] * 0.5;
    double y3 = 1.0 * sin(((2 * M_PI) * 2.2) * _t) + [SCDRandomUtil nextDouble] * 0.5;
    
    [_ds1 appendX:@(_t) y:@(y1)];
    [_ds2 appendX:@(_t) y:@(y2)];
    [_ds3 appendX:@(_t) y:@(y3)];
    
    _t += OneOverTimeInterval;

    id<ISCIAxis> xAxis = [self.surface.xAxes itemAt:0];
    if (_t > VisibleRangeMax) {
        [xAxis.visibleRange setDoubleMinTo:xAxis.visibleRange.minAsDouble + OneOverTimeInterval maxTo:xAxis.visibleRange.maxAsDouble + OneOverTimeInterval];
    }
}

- (void)resetChart {
    [SCIUpdateSuspender usingWithSuspendable:self.surface withBlock:^{
        [self->_ds1 clear];
        [self->_ds2 clear];
        [self->_ds3 clear];
    }];
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    
    [_timer invalidate];
    _timer = nil;
}

@end
FIFOSpeedTestSciChart.swift
View source code
//******************************************************************************
// SCICHART® Copyright SciChart Ltd. 2011-2019. All rights reserved.
//
// Web: http://www.scichart.com
// Support: support@scichart.com
// Sales:   sales@scichart.com
//
// FifoScrollingChartView.swift is part of the SCICHART® Examples. Permission is hereby granted
// to modify, create derivative works, distribute and publish any part of this source
// code whether for commercial, private or personal use.
//
// The SCICHART® examples are distributed in the hope that they will be useful, but
// without any warranty. It is provided "AS IS" without warranty of any kind, either
// expressed or implied.
//******************************************************************************

private let FifoCapacicty: Int = 50
private let TimeInterval = 30.0
private let OneOverTimeInterval = 1.0 / 30
private let VisibleRangeMax = 50.0 * 1.0 / 30.0
private let GrowBy = 50.0 * 1.0 / 30.0 * 0.1

class FifoScrollingChartView: SCDSingleChartViewController<SCIChartSurface> {
    
    private var _timer: Timer?
    private var _ds1: SCIXyDataSeries!
    private var _ds2: SCIXyDataSeries!
    private var _ds3: SCIXyDataSeries!
    
    private var _t = 0.0
    private var _isRunning = false
    
    override var associatedType: AnyClass { return SCIChartSurface.self }
    
    override func provideExampleSpecificToolbarItems() -> [ISCDToolbarItem] {
        return [SCDToolbarButtonsGroup(toolbarItems: [
            SCDToolbarButton(title: "Start", image: SCIImage(named: "chart.play"), andAction: { [weak self] in self?._isRunning = true }),
            SCDToolbarButton(title: "Pause", image: SCIImage(named: "chart.pause"), andAction: { [weak self] in self?._isRunning = false }),
            SCDToolbarButton(title: "Stop", image: SCIImage(named: "chart.stop"), andAction: { [weak self] in
                self?._isRunning = false
                self?.resetChart()
            })
        ])]
    }
    
    override func initExample() {
        let xAxis = SCINumericAxis()
        xAxis.autoRange = .never
        xAxis.visibleRange = SCIDoubleRange.init(min: -GrowBy, max: VisibleRangeMax + GrowBy)
        
        let yAxis = SCINumericAxis()
        yAxis.autoRange = .always

        _ds1 = SCIXyDataSeries(xType: .double, yType: .double)
        _ds1.fifoCapacity = FifoCapacicty
        _ds2 = SCIXyDataSeries(xType: .double, yType: .double)
        _ds2.fifoCapacity = FifoCapacicty
        _ds3 = SCIXyDataSeries(xType: .double, yType: .double)
        _ds3.fifoCapacity = FifoCapacicty
        
        let rSeries1 = SCIFastLineRenderableSeries()
        rSeries1.dataSeries = _ds1
        rSeries1.strokeStyle = SCISolidPenStyle(color: 0xFFe97064, thickness: 2)
        
        let rSeries2 = SCIFastLineRenderableSeries()
        rSeries2.dataSeries = _ds2
        rSeries2.strokeStyle = SCISolidPenStyle(color: 0xFF47bde6, thickness: 2)
        
        let rSeries3 = SCIFastLineRenderableSeries()
        rSeries3.dataSeries = _ds3
        rSeries3.strokeStyle = SCISolidPenStyle(color: 0xFFae418d, thickness: 2)
        
        SCIUpdateSuspender.usingWith(surface) {
            self.surface.xAxes.add(xAxis)
            self.surface.yAxes.add(yAxis)
            self.surface.renderableSeries.add(rSeries1)
            self.surface.renderableSeries.add(rSeries2)
            self.surface.renderableSeries.add(rSeries3)
        }
        
        _timer = Timer.scheduledTimer(timeInterval: TimeInterval / 1000, target: self, selector: #selector(updateData), userInfo: nil, repeats: true)
        if let timer = _timer {
            RunLoop.main.add(timer, forMode: .common)
        }
        _isRunning = true
    }
    
    @objc func updateData(_ timer: Timer) {
        if (!_isRunning) { return }
        
        let y1: Double = 3.0 * sin(((2 * .pi) * 1.4) * _t) + SCDRandomUtil.nextDouble() * 0.5
        let y2: Double = 2.0 * cos(((2 * .pi) * 0.8) * _t) + SCDRandomUtil.nextDouble() * 0.5
        let y3: Double = 1.0 * sin(((2 * .pi) * 2.2) * _t) + SCDRandomUtil.nextDouble() * 0.5
        
        _ds1.append(x: _t, y: y1)
        _ds2.append(x: _t, y: y2)
        _ds3.append(x: _t, y: y3)
        
        _t += OneOverTimeInterval;
        
        let xAxis = surface.xAxes.item(at: 0)
        if (_t > VisibleRangeMax) {
            xAxis.visibleRange.setDoubleMinTo(xAxis.visibleRange.minAsDouble + OneOverTimeInterval, maxTo: xAxis.visibleRange.maxAsDouble + OneOverTimeInterval)
        }
    }
    
    fileprivate func resetChart() {
        SCIUpdateSuspender.usingWith(surface) {
            self._ds1.clear()
            self._ds2.clear()
            self._ds3.clear()
        }
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        
        _timer?.invalidate()
        _timer = nil
    }
}
Back to iOS & macOS charts Examples