Search code examples
iosxcodepie-chartshinobi

Shinobi iOS : Pie chart spoke with value and name


I would like to create pie chart with a little bit custom spokes.

What I would like to achieved is to display name od datapoint above spoke and value below spoke. Right now I know how to do it both above with

func sChart(_ chart: ShinobiChart, labelForSliceAt sliceIndex: Int, in series: SChartRadialSeries) -> UILabel?

and set label number of line to 2....enter image description here

How to put one label below and one above?

Mark


Solution

  • I've answered your question over at our shinobicontrols forum but am pasting my answer here in case anyone comes across this post:

    If you weren't using spokes, then the way to do this would be to use the delegate method sChart:alterLabel:forDatapoint:atSliceIndex:inRadialSeries: to reposition the label, but unfortunately there's currently an issue which means that when displaying spokes, this method is called before the labels are positioned.

    We'll try to get the issue fixed for a future release of shinobicharts but in the meantime you can work around the issue by creating your own additional labels and using KVO, like this:

    func sChart(_ chart: ShinobiChart, labelForSliceAt sliceIndex: Int, in series: SChartRadialSeries) -> UILabel {
        let pieSeries = series as! SChartPieSeries
        let dp = self.sChart(chart, dataPointAt: sliceIndex, forSeriesAt: 0)
        let label = UILabel()
        label.text = String(format:"Slice %i", sliceIndex)
        label.sizeToFit()
        label.addObserver(self, forKeyPath: "frame", options: NSKeyValueObservingOptions(rawValue: 0), context: nil)
        label.addObserver(self, forKeyPath: "center", options: NSKeyValueObservingOptions(rawValue: 0), context: nil)
        label.layer.addObserver(self, forKeyPath: "frame", options: NSKeyValueObservingOptions(rawValue: 0), context: nil)
        label.layer.addObserver(self, forKeyPath: "center", options: NSKeyValueObservingOptions(rawValue: 0), context: nil)
    
        sliceLabels[sliceIndex] = label
    
        let customLabel = UILabel()
        customLabel.text = String(format:"Value: %.0f", dp.sChartYValue() as! Float)
        customLabel.font = pieSeries.style().labelFont
        customLabel.textColor = pieSeries.style().labelFontColor
        customLabel.backgroundColor = pieSeries.style().labelBackgroundColor
        customLabel.sizeToFit()
        chart.canvas.overlay.addSubview(customLabel);
        customLabels[sliceIndex] = customLabel;
    
        return label;
    }
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if let changedLabel = object as? UILabel {
            for (index, label) in sliceLabels {
                if let sliceLabel = label as? UILabel, sliceLabel === changedLabel {
                    let customLabel = customLabels[index] as! UILabel
                    customLabel.center = CGPoint(x: sliceLabel.center.x,
                                                 y: sliceLabel.center.y + sliceLabel.frame.size.height);
                }
            }
        }
    }
    

    Note that as explained here UIKit is not officially KVO compliant, but most users on that post agree that the code suggested there does work. (You may wish to add more observers than I've added here.)

    I apologise for the rather long workaround, but I hope it helps you.