Home iOS Development ios – How to prevent BarChartView labels from overlapping?

ios – How to prevent BarChartView labels from overlapping?

0

[ad_1]

I am using the BarChartView from the iOS-Charts library. However, when I stack the bars, sometimes the labels for these bars overlap. Is there a way to prevent this from happening? Some ideas I had:

  1. Is it possible to position these labels on either side of the bars?
  2. Is it possible to only display a label if it is not 0?
  3. Is it possible to scale the bar size down if it is way larger (like the first bar in the image)?

Image showing different bars in BarChartView with labels that overlap when bars are small.

Here is my code:

import UIKit
import Charts

struct BarChartInformation: Decodable {
    public let title: String?
    public let isStacked: Bool?
    public let numberDataType: String?
    public let data: [SingleBarChartInformation]?
    private enum CodingKeys: String, CodingKey {
        case title = "title"
        case isStacked = "isStacked"
        case numberDataType = "numberDataType"
        case data = "data"
    }
}

struct SingleBarChartInformation: Decodable {
    public let title: String?
    public let values: [Double]?
    public let labels: [String]?
    public let color: String?
    private enum CodingKeys: String, CodingKey {
        case title = "title"
        case values = "values"
        case labels = "labels"
        case color = "color"
    }
}

class BarChartTableViewCell: BorderCardTableViewCell {
    
    let barChart = BarChartView(frame: .zero)
    let titleLabel = UILabel(frame: .zero)
    
    var cellData: BarChartInformation? {
        didSet {
            guard let cellData = cellData else { return }
            let data = ChartHelpers().makeCustomBarChart(chartData: cellData)
            var axisSize = Double((cellData.data?.get(index: 0)?.values?.count ?? 0)) - 0.5
            let labels = cellData.data?.get(index: 0)?.labels
            
            var finalLabels = [String]()
            for label in labels ?? [String]() {
                if cellData.isStacked == false {
                    for _ in 0...(cellData.data?.count ?? 2)-2 {
                        finalLabels.append("")
                    }
                }
                finalLabels.append(label)
            }
            
            if cellData.isStacked == false {
                axisSize *= Double(cellData.data?.count ?? 1)
                barChart.xAxis.axisMinimum = 0
            }
            
            barChart.xAxis.axisMaximum = axisSize
            barChart.xAxis.axisMaxLabels = Int(axisSize)
            barChart.xAxis.valueFormatter = IndexAxisValueFormatter(values: finalLabels)
            barChart.xAxis.granularityEnabled = true
            barChart.xAxis.granularity = 1
            barChart.data = data
            barChart.xAxis.labelPosition = .bottom
            titleLabel.text = cellData.title
            self.contentView.layoutIfNeeded()
        }
    }
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        addTimelineChart()
        addLabels()
        self.contentView.layoutIfNeeded()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    func addTimelineChart() {
        borderView.addSubview(barChart)
        barChart.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            barChart.topAnchor.constraint(equalTo: self.borderView.topAnchor, constant: 58),
            barChart.bottomAnchor.constraint(equalTo: self.borderView.bottomAnchor),
            barChart.rightAnchor.constraint(equalTo: self.borderView.rightAnchor),
            barChart.leftAnchor.constraint(equalTo: self.borderView.leftAnchor),
        ])
        barChart.animate(xAxisDuration: 0.5, yAxisDuration: 0.5)
        barChart.xAxis.labelTextColor = .label
        barChart.xAxis.labelPosition = .bottom
        barChart.xAxis.drawLabelsEnabled = true
        barChart.xAxis.drawGridLinesEnabled = false
        barChart.chartDescription?.enabled = true
        barChart.xAxis.enabled = true
        barChart.legend.enabled = true
        barChart.rightAxis.enabled = false
        barChart.leftAxis.enabled = false
        barChart.leftAxis.drawGridLinesEnabled = false
        barChart.rightAxis.drawGridLinesEnabled = false
        barChart.xAxis.drawGridLinesEnabled = false
        barChart.doubleTapToZoomEnabled = false
        barChart.backgroundColor = .clear
        barChart.dragEnabled = false
        barChart.highlightFullBarEnabled = false
        barChart.contentMode = .scaleAspectFit
    }
    
    func addLabels() {
        titleLabel.font = Constants.Fonts.bold3
        titleLabel.textColor = .label
        titleLabel.textAlignment = .left
        borderView.addSubview(titleLabel)
        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            titleLabel.topAnchor.constraint(equalTo: borderView.topAnchor, constant: 4),
            titleLabel.leftAnchor.constraint(equalTo: borderView.leftAnchor, constant: 12),
            titleLabel.heightAnchor.constraint(equalToConstant: 26),
        ])
        
        let line = UIView(frame: .zero)
        line.backgroundColor = Constants.borderColor
        borderView.addSubview(line)
        line.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            line.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 2),
            line.rightAnchor.constraint(equalTo: borderView.rightAnchor, constant: -Constants.borderWidth),
            line.leftAnchor.constraint(equalTo: borderView.leftAnchor, constant: Constants.borderWidth),
            line.heightAnchor.constraint(equalToConstant: Constants.borderWidth),
        ])
    }
}


func makeCustomBarChart(chartData: BarChartInformation) -> BarChartData {
    var dataSets: [BarChartDataSet] = []
    let stacked: Bool = chartData.isStacked ?? true
        
    for i in 0..<(chartData.data?.count ?? 0) {
        var entries: [BarChartDataEntry] = []
        for ii in 0..<(chartData.data?.get(index: i)?.values?.count ?? 0) {
            guard let data = chartData.data, let values = data.get(index: i)?.values else {
                return BarChartData(dataSets: [])
            }
            let dataEntry = BarChartDataEntry(x: Double(ii), y: values.get(index: ii) ?? 0)
            entries.append(dataEntry)
        }
    let dataSet = BarChartDataSet(entries: entries, label: chartData.data?.get(index: i)?.title)
    if let color = chartData.data?.get(index: i)?.color {
        dataSet.setColor(UIColor(hexString: color))
    } else {
       dataSet.setColor(UIColor(hexString: "#FFFFFF"))
    }
       dataSets.append(dataSet)
    }
        
    let data = BarChartData(dataSets: dataSets)
    if !stacked {
       data.groupBars(fromX: 0, groupSpace: 0, barSpace: 0)
    } else {
            
    }
        
    let formatter = NumberFormatter()
    formatter.numberStyle = String.asNumberStyle(string: chartData.numberDataType)
    formatter.maximumFractionDigits = 0
    formatter.multiplier = 1.0
    data.setValueFormatter(DefaultValueFormatter(formatter:formatter))
        
    return data
}

[ad_2]