Pre loader

Custom Axis Labels with symbol images using LabelProvider and NSAttributedString

Welcome to the SciChart Forums!

  • Please read our Question Asking Guidelines for how to format a good question
  • Some reputation is required to post answers. Get up-voted to avoid the spam filter!
  • We welcome community answers and upvotes. Every Q&A improves SciChart for everyone

WPF Forums | JavaScript Forums | Android Forums | iOS Forums

0
0

Hi there,

I’m trying to display little icons as axis labels using the LabelProvider API and NSAttributedString (with NSTextAttachmet). Is this supported? Here’s a minimal example:

import UIKit
import Foundation
import SciChart
import SciChart.Protected.SCILabelProviderBase

class ViewController: UIViewController {

    private lazy var chart: SCIChartSurface = {
        let c = SCIChartSurface(frame: .zero)
        c.xAxes.add(items: SCINumericAxis())

        let yAxis = SCINumericAxis()
        yAxis.labelProvider = SymbolLabelProvider()
        c.yAxes.add(items: yAxis)
        return c
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        SCIChartSurface.setRuntimeLicenseKey(myLicenseKey)

        view.addSubview(chart)
        chart.translatesAutoresizingMaskIntoConstraints = false
        let guide = self.view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            chart.leadingAnchor.constraint(equalTo: guide.leadingAnchor),
            chart.trailingAnchor.constraint(equalTo: guide.trailingAnchor),
            chart.topAnchor.constraint(equalTo: guide.topAnchor),
            chart.bottomAnchor.constraint(equalTo: guide.bottomAnchor),
        ])
    }
}

class SymbolLabelProvider: SCILabelProviderBase<SCINumericAxis> {

    lazy var numberFormatter: NumberFormatter = {
        let f = NumberFormatter()
        f.allowsFloats = true
        f.maximumFractionDigits = 2
        return f
    }()

    init() {
        super.init(axisType: ISCINumericAxis.self)
    }

    override func formatLabel(_ dataValue: ISCIComparable!) -> ISCIString! {

        let intValue = Int(dataValue.toDouble())
        let font = UIFont.init(descriptor: axis.tickLabelStyle.fontDescriptor, size: UIFont.systemFontSize * 4)

        if intValue.isMultiple(of: 2) {
            let i = UIImage(systemName: "circle", withConfiguration: UIImage.SymbolConfiguration(font: font))
            return NSAttributedString(attachment: NSTextAttachment(image: i!))
        } else {
            let attributes: [NSAttributedString.Key: Any] = [
                .font: font,
                .foregroundColor: UIColor.yellow,
            ]
            return NSAttributedString(string: numberFormatter.string(for: dataValue.toDouble())!, attributes: attributes)
        }
    }
}

See attached screenshot for the result.

If this is not supported: any suggestions / ideas for a workaround?

Thanks
—Matthias

Version
4.1.0.5498
Images
  • You must to post comments
1
0

Hi, Matthias.

Unfortunately, there is no way to draw NSTextAttachment on a CGContext. For more details, see this Stackverflow thread

What you can do is kind of a hack:

  1. Add few spacing characters in front of your axis label to make the axis larger to have space for some additional drawing

    class SymbolLabelProvider: SCINumericLabelProvider {
        override func formatLabel(_ dataValue: ISCIComparable!) -> ISCIString! {
            return ("    \(super.formatLabel(dataValue)!)") as ISCIString
        }
    }
    
  2. Subclass your axis, create a texture from your image and draw it in the “onDrawAxis” method, like this:

    import SciChart.Protected.SCIAxisBase
    
    class CustomDrawingAxis: SCINumericAxis {
    
         private lazy var imageBitmap = SCIBitmap(image: #imageLiteral(resourceName: "image.weather.storm"))
    
        override func onDrawAxis(with renderContext: ISCIRenderContext2D, andAssetManager assetManager: ISCIAssetManager2D) {
             super.onDrawAxis(with: renderContext, andAssetManager: assetManager)
    
             for coordinate in tickCoordinatesProvider.tickCoordinates.majorTickCoordinates.itemsArray {
                 let texture = assetManager.texture(with: imageBitmap)
    
                 renderContext.draw(texture, in: CGRect(x: CGFloat(layoutRect.minX + 5), y: CGFloat(coordinate -  5), width: 10, height: 10))
             }
         }
    }
    

It will produce the following result. See the screenshot.

Let us know if it works for you.
Thanks in advance.

Images
  • You must to post comments
Showing 1 result
Your Answer

Please first to submit.