iOS Charting Documentation - SciChart
Tutorial 09 - Adding Multiple Charts

In our series of tutorials, up until now we have added a chart with two YAxis, one XAxis, two series, added tooltips, legends, zooming and panning behavior, and added some custom annotations.
Next, we are going to show you how to create multiple charts and link them together.

Revision

If you haven't already tried you will need to review the following tutorials, as we're working straight from these:
Tutorial 07 - Adding Annotations
Tutorial 08 - Adding Multiple Axis
Or, if you're in a hurry, skim through the tutorials and copy paste the code into a new project.

Tutorials Repository

As it was mentioned previously - we've created Git repository with all Tutorials. So you can clone/download the appropriate project you need!

Once you've done that, we are ready to get started.

Adding a Second SciChartSurface

There is no restriction on the number of SciChartSurfaces you can have in an application. In this tutorial we are going to add a second SciChartSurface.
Let's start by adding a second SciChartSurface:

Second chart properties
Copy Code
namespace AddingMultipleSurfaces
{
    public partial class ViewController : UIViewController
    {
        private SCIChartSurface _surfaceTop;
        private SCIChartSurface _surfaceBottom;
     ...

Next, let's initialize these variables and layout our surfaces in ViewController's view just like in the following code. After all, our viewDidLoad method should have following view:

Initializing chart surfaces
Copy Code
        public override void ViewDidLoad()
        {
            base.ViewDidLoad();
            this.View.BackgroundColor = UIColor.DarkGray;
            // Perform any additional setup after loading the view, typically from a nib.
            _surfaceTop = new SCIChartSurface();
            _surfaceTop.TranslatesAutoresizingMaskIntoConstraints = false;
            //_surfaceTop.AutoresizingMask = UIViewAutoresizing.FlexibleDimensions;
            _surfaceBottom = new SCIChartSurface();
            _surfaceBottom.TranslatesAutoresizingMaskIntoConstraints = false;
            //_surfaceBottom.AutoresizingMask = UIViewAutoresizing.FlexibleDimensions;
            this.View.AddSubview(_surfaceTop);
            this.View.AddSubview(_surfaceBottom);
            var viewsDictionary = NSDictionary.FromObjectsAndKeys(new NSObject[] { _surfaceTop, _surfaceBottom }, new NSObject[] { new NSString("_surfaceTop"), new NSString "_surfaceBottom") });
            this.View.AddConstraints(NSLayoutConstraint.FromVisualFormat("H:|-[_surfaceTop]-|", 0, new NSDictionary(), viewsDictionary));
            this.View.AddConstraints(NSLayoutConstraint.FromVisualFormat("V:|-[_surfaceTop]-(==1)-[_surfaceBottom(==_surfaceTop)]-|", 0, new NSDictionary(), viewsDictionary));
            this.View.AddConstraints(NSLayoutConstraint.FromVisualFormat("|-[_surfaceBottom(==_surfaceTop)]-|", 0, new NSDictionary(), viewsDictionary));
            AddAxes(_surfaceTop);
            AddAxes(_surfaceBottom);
            CreateDataSeries();
            CreateRenderableSeries();
            AddModifiers();
        }

You probably noticed AddAxes() method - to make code a little bit easier to read - we moved adding axes logic to that method:

AddAxes() method
Copy Code
private void AddAxes(SCIChartSurface surface)
{
   surface.XAxes.Add(new SCINumericAxis() { GrowBy = new SCIDoubleRange(0.1, 0.1)});
   var yAxis = new SCINumericAxis();
   yAxis.GrowBy = new SCIDoubleRange(min: 0.1, max: 0.1);
   yAxis.AxisId = "firstYAxis";
   surface.YAxes.Add(yAxis);
   var yLeftAxis = new SCINumericAxis();
   yLeftAxis.AxisId = "secondaryYAxis";
   yLeftAxis.AxisAlignment = SCIAxisAlignment.Left;
   yLeftAxis.VisibleRange = new SCIDoubleRange(min: -2, max: 2);
   yLeftAxis.AutoRange = SCIAutoRange.Never;
   surface.YAxes.Add(yLeftAxis);
}

If you run the application at this point, you will see our first Realtime chart is displayed at the top of the application, and the second chart 'chartSurfaceBottom' is static, and displayed underneath.

Adding Series to the Second Chart

The second chart needs a series, so we are going to add a Mountain Series. Add the following series to the chart in CreateRenderableSeries() method :

СreateRenderableSeries() method
Copy Code
        void CreateRenderableSeries()
        {
            _lineRenderableSeries = new SCIFastLineRenderableSeries();
            _lineRenderableSeries.DataSeries = _lineDataSeries;
            _lineRenderableSeries.YAxisId = "firstYAxis";
            _scatterRenderableSeries = new SCIXyScatterRenderableSeries();
            _scatterRenderableSeries.DataSeries = _scatterDataSeries;
            _scatterRenderableSeries.YAxisId = "secondaryYAxis";
            _mountainRenderableSeries = new SCIFastMountainRenderableSeries();
            _mountainRenderableSeries.DataSeries = _lineDataSeries;
            _mountainRenderableSeries.YAxisId = "firstYAxis";
            _surfaceTop.RenderableSeries.Add(_lineRenderableSeries);
            _surfaceTop.RenderableSeries.Add(_scatterRenderableSeries);
            _surfaceBottom.RenderableSeries.Add(_mountainRenderableSeries);
        }

As you might have noticed, we are using the same _lineDataSeries we used for _lineRendereableSeries, so ou don't need to create new instance of Data Series and fill it with some data.

Now run the application. It doesn't look right, does it? The top chart is scrolling but the bottom chart is not scrolling.
To fix that, what we need to do is just to update the ViewDidLoad method just as following:

ViewDidLoad() method
Copy Code
        public override void ViewWillAppear(bool animated)
        {
            base.ViewWillAppear(animated);
            if (_timer == null)
            {
                _timer = NSTimer.CreateRepeatingScheduledTimer(0.01, (timer) =>
                {
                    _i++;
                    _lineDataSeries.Append(_i, Math.Sin(_i * 0.1 + _phase));
                    _scatterDataSeries.Append(_i, Math.Cos(_i * 0.1 + _phase));
                    _phase += 0.01;
                    var customAnnotation = new SCICustomAnnotation();
                    if (_i % 100 == 0)
                    {
                        customAnnotation.CustomView = new UILabel(new CoreGraphics.CGRect(0, 0, 10, 10)) { Text = "Y", BackgroundColor = UIColor.LightGray };
                        customAnnotation.X1Value = _i;
                        customAnnotation.Y1Value = 0.5;
                        customAnnotation.CoordinateMode = SCIAnnotationCoordinateMode.Absolute;
                        customAnnotation.YAxisId = "firstYAxis";
                        // adding new custom annotation into the annotationGroup property
                        _annotationCollection.Add(customAnnotation);
                        // removing annotations that are out of visible range
                        var customAn = _annotationCollection[0] as SCICustomAnnotation;
                        if (customAn != null && (double)customAn.X1Value < (_i - 500))
                        {
                            // since the contentView is UIView element - we have to call removeFromSuperView method to remove it from screen
                            customAn.CustomView.RemoveFromSuperview();
                            _annotationCollection.Remove(customAn);
                        }
                    }
                    if (_i % 200 == 0)
                    {
                        customAnnotation.YAxisId = "secondaryYAxis";
                    }
                      
                    _surfaceTop.ZoomExtentsX();
                    // Extents bottom chart 
                    _surfaceBottom.ZoomExtents();
                });
            }
        }

Now run the example again. That's better!

Syncing charts

The next thing we are going to do is to synchronise both charts. We would need one additional property:
SCIMultiSurfaceModifier variable
Copy Code
namespace AddingMultipleSurfaces
{
    public partial class ViewController : UIViewController
    {
        private SCIChartSurface _surfaceTop;
        private SCIChartSurface _surfaceBottom;
        private SCIMultiSurfaceModifier _szem;
        ...

As well as its initialization:

SCIMultiSurfaceModifier property initialization
Copy Code
        public override void ViewDidLoad()
        {
            base.ViewDidLoad();
            this.View.BackgroundColor = UIColor.DarkGray;
            // Perform any additional setup after loading the view, typically from a nib.
            _surfaceTop = new SCIChartSurface();
            _surfaceTop.TranslatesAutoresizingMaskIntoConstraints = false;
            _surfaceBottom = new SCIChartSurface();
            _surfaceBottom.TranslatesAutoresizingMaskIntoConstraints = false;
            _szem = new SCIMultiSurfaceModifier(GetClassForType(typeof(SCIZoomExtentsModifier)));

And finally, we have to update the method where we add chart modifiers:

updated AddModifiers()
Copy Code
         void AddModifiers()
        {
            var xDragModifierSync = new SCIMultiSurfaceModifier(GetClassForType(typeof(SCIXAxisDragModifier)));
            var pinchZoomModifierSync = new SCIMultiSurfaceModifier(GetClassForType(typeof(SCIPinchZoomModifier)));
            var panZoomModifierSync = new SCIMultiSurfaceModifier(GetClassForType(typeof(SCIZoomPanModifier)));
            var xAxisDragmodifier = xDragModifierSync.ModifierForSurface(_surfaceTop) as SCIXAxisDragModifier;
            if (xAxisDragmodifier != null)
            {
                xAxisDragmodifier.DragMode = SCIAxisDragMode.Pan;
                xAxisDragmodifier.ClipModeX = SCIClipMode.None;
            }
            var pinchZoomModifier = pinchZoomModifierSync.ModifierForSurface(_surfaceTop);
            var legendCollectionModifier = new SCILegendModifier();
            var groupModifier = new SCIChartModifierCollection();
            if (xAxisDragmodifier != null) groupModifier.Add(xAxisDragmodifier);
            if (pinchZoomModifier != null) groupModifier.Add(pinchZoomModifier);
            groupModifier.Add(legendCollectionModifier);
            _surfaceTop.ChartModifiers = groupModifier;
            var xAxisDragmodifierBottom = xDragModifierSync.ModifierForSurface(_surfaceBottom) as SCIXAxisDragModifier;
            if (xAxisDragmodifierBottom != null)
            {
                xAxisDragmodifierBottom.DragMode = SCIAxisDragMode.Pan;
                xAxisDragmodifierBottom.ClipModeX = SCIClipMode.None;
            }
            var pinchZoomModifierBottom = pinchZoomModifierSync.ModifierForSurface(_surfaceBottom);
            var groupModifierBottom = new SCIChartModifierCollection();
            if (xAxisDragmodifierBottom != null) groupModifierBottom.Add(xAxisDragmodifierBottom);
            if (pinchZoomModifierBottom != null) groupModifierBottom.Add(pinchZoomModifierBottom);
            _surfaceBottom.ChartModifiers = groupModifierBottom;
        }