iOS Charting Documentation - SciChart
Tutorial 07 - Adding Annotations

In the prior tutorials we have familiarized with a great and powerful SciChart functionality. In this one we are going to play around with one more cool thing SciChart supports - Annotations!

We are going to build on our previous Tutorial 06 - Adding RealTime Updates to add annotations to a chart in a realtime context.
But, let's take a look at how can we add simple annotation like Line, Box or Text.

Adding simple annotations

Let's add simple Text annotation with "Hello SciChart world" text. To do that, we only need to create instance of TextAnnotation and attach to the scichart surface instance:

adding Text annotation
Copy Code
        let textAnnotation = SCITextAnnotation()
        textAnnotation.xAxisId = "xAxis"
        textAnnotation.yAxisId = "yAxis"
        textAnnotation.coordinateMode = .relative
        textAnnotation.x1 = SCIGeneric(0.7)
        textAnnotation.y1 = SCIGeneric(0.5)
       
        textAnnotation.text = "Hello SciChart world"
        textAnnotation.style.textStyle.fontSize = 18
        textAnnotation.style.textColor = UIColor.white
        textAnnotation.style.backgroundColor = UIColor.clear
        chartSurface?.annotations = textAnnotation

Adding custom annotation

Before we will start to work on creating custom annotations, let's check we are on the same page. The source code of our ViewController class should look like as follow:

ViewController class
Copy Code
import UIKit
import SciChart
class ViewController: UIViewController {
    
    var sciChartSurface: SCIChartSurface?
    
    var lineDataSeries: SCIXyDataSeries!
    var scatterDataSeries: SCIXyDataSeries!
    
    var lineRenderableSeries: SCIFastLineRenderableSeries!
    var scatterRenderableSeries: SCIXyScatterRenderableSeries!
    
    var timer: Timer?
    var phase = 0.0
    var i = 0
    let totalCapacity = 500.0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        sciChartSurface = SCIChartSurface(frame: self.view.bounds)
        sciChartSurface?.autoresizingMask = [.flexibleHeight, .flexibleWidth]
        sciChartSurface?.translatesAutoresizingMaskIntoConstraints = true
        
        self.view.addSubview(sciChartSurface!)
        
        let xAxis = SCINumericAxis()
        xAxis.growBy = SCIDoubleRange(min: SCIGeneric(0.1), max: SCIGeneric(0.1))
        sciChartSurface?.xAxes.add(xAxis)
        
        // adding some paddding for Y axis
        let yAxis = SCINumericAxis()
        yAxis.growBy = SCIDoubleRange(min: SCIGeneric(0.1), max: SCIGeneric(0.1))
        sciChartSurface?.yAxes.add(yAxis)
        
        createDataSeries()
        createRenderableSeries()
        addModifiers()
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        if nil == timer{
            timer = Timer.scheduledTimer(withTimeInterval: 0.001, repeats: true, block: updatingDataPoints)
        }
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
        // invalidating timer
        // Timer is a class which is not under ARC umrella, so we need to control it by ourselves
        if nil != timer{
            timer?.invalidate()
            timer = nil
        }
    }
    
    func updatingDataPoints(timer:Timer){
        
        i += 1
        
        // appending new data points into the line and scatter data series
        lineDataSeries.appendX(SCIGeneric(i), y: SCIGeneric(sin(Double(i)*0.1 + phase)))
        scatterDataSeries.appendX(SCIGeneric(i), y: SCIGeneric(cos(Double(i)*0.1 + phase)))
        
        phase += 0.01        
        sciChartSurface?.zoomExtents()
    }
    
    func createDataSeries(){
        // Init line data series
        lineDataSeries = SCIXyDataSeries(xType: .int16, yType: .double)
        lineDataSeries.fifoCapacity = Int32(totalCapacity)
        lineDataSeries.seriesName = "line series"
        
        // Init scatter data series
        scatterDataSeries = SCIXyDataSeries(xType: .int16, yType: .double)
        scatterDataSeries.fifoCapacity = Int32(totalCapacity)
        scatterDataSeries.seriesName = "scatter series"
        
        for i in 0...Int32(totalCapacity){
            lineDataSeries.appendX(SCIGeneric(i), y: SCIGeneric(sin(Double(i)*0.1)))
            scatterDataSeries.appendX(SCIGeneric(i), y: SCIGeneric(cos(Double(i)*0.1)))
        }
        
        i = Int(lineDataSeries.count())
    }
    
    func createRenderableSeries(){
        lineRenderableSeries = SCIFastLineRenderableSeries()
        lineRenderableSeries.dataSeries = lineDataSeries
        
        scatterRenderableSeries = SCIXyScatterRenderableSeries()
        scatterRenderableSeries.dataSeries = scatterDataSeries
        
        sciChartSurface?.renderableSeries.add(lineRenderableSeries)
        sciChartSurface?.renderableSeries.add(scatterRenderableSeries)
    }
    
    func addModifiers(){
        let xAxisDragmodifier = SCIXAxisDragModifier()
        xAxisDragmodifier.dragMode = .pan
        xAxisDragmodifier.clipModeX = .none
        
        let yAxisDragmodifier = SCIYAxisDragModifier()
        yAxisDragmodifier.dragMode = .pan
        
        let extendZoomModifier = SCIZoomExtentsModifier()
        let pinchZoomModifier = SCIPinchZoomModifier()
        
        let rolloverModifier = SCIRolloverModifier()
        let legend = SCILegendModifier()
        
        let groupModifier = SCIChartModifierCollection(childModifiers: [xAxisDragmodifier, yAxisDragmodifier, pinchZoomModifier, extendZoomModifier, legend, rolloverModifier])
        
        sciChartSurface?.chartModifiers = groupModifier
    }
    
}

And the application should work just like this:

Next: Adding the Custom Annotations to the Chart

The SciChart framework you are currantly using already has a CustomAnnotation class. Pretty simple at a glance, it allows you to use your custom annotation - whatever class inherited from UIVIew class. For example, let's say, we would like to have a custom "text" annotation with "Y" text and place it on every X=100 position. In this case what we need is to declare the UILabel instance and set it to the SCICustomAnnotation's contentView property, just like in the following code:

ViewController class
Copy Code
import UIKit
import SciChart
class ViewController: UIViewController {
    
    ...
    
    // annotation collection property, used to store all annotations
    let annotationGroup = SCIAnnotationCollection()
   
    override func viewDidLoad() {
        ...
        // set chartSurface's annotation property to annotationGroup
        sciChartSurface?.annotations = annotationGroup
    }
    
    func updatingDataPoints(timer:Timer){
        
        ...
        
        if (i%100 == 0){
           
            let customAnnotation = SCICustomAnnotation()
            let customAnnotationContentView = UILabel(frame: CGRect.init(x: 0, y: 0, width: 10, height: 10))
            customAnnotationContentView.text = "Y"
            customAnnotationContentView.backgroundColor = UIColor.lightGray
           
            customAnnotation.contentView = customAnnotationContentView
            customAnnotation.x1 = SCIGeneric(i)
            customAnnotation.y1 = SCIGeneric(0.5)
            customAnnotation.coordinateMode = .relativeY
           
            // adding new custom annotation into the annotationGroup property
            annotationGroup.addItem(customAnnotation)
           
            // removing annotations that are out of visible range
            let customAn = annotationGroup.item(at: 0) as! SCICustomAnnotation
           
            if(SCIGenericDouble(customAn.x1) < Double(i) - totalCapacity){
                // since the contentView is UIView element - we have to call removeFromSuperView method to remove it from screen
                customAn.contentView.removeFromSuperview()
                annotationGroup.removeItem(customAn)
            }
        }
    }
    
   ...
   
}

As a result we should now see an application with following behavior:

Further reading

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!