Quick Start Guide > Tutorials (Swift3) > Tutorial 07 - Adding Annotations
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

Source code for this tutorial can be found at our Github Repository under v2.x > Tutorials

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 a CustomAnnotation, 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:

Adding Custom Annotations to the Chart

The SciChart framework you are currantly using already has a CustomAnnotation class. Basically, this allows you to use put any class inherited from UIVIew into the chart. 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:

 

See Also