user2017279
user2017279

Reputation: 80

Charts: Get selected bar data only when tapped inside the bar

I used the https://github.com/danielgindi/Charts library in my project. It is working perfectly but I want to select bar data only when user taps inside the bar. Currently it will return bar data on outside the tap also. It is providing the nearest bar data to the tap when user tap outside the bar.

I set up the bar chart like this :

_chartView.delegate = self;

_chartView.drawBarShadowEnabled = NO;
_chartView.drawValueAboveBarEnabled = YES;

_chartView.maxVisibleValueCount = 60;

ChartXAxis *xAxis = _chartView.xAxis;
xAxis.labelPosition = XAxisLabelPositionBottom;
xAxis.labelTextColor = [UIColor whiteColor];
xAxis.labelFont = [UIFont systemFontOfSize:20.f];
xAxis.drawGridLinesEnabled = NO;
xAxis.spaceBetweenLabels = 2.0;
xAxis.labelWidth = 100;

ChartYAxis *leftAxis = _chartView.leftAxis;
leftAxis.labelFont = [UIFont systemFontOfSize:20.f];
leftAxis.labelCount = 5;
leftAxis.labelTextColor = [UIColor whiteColor];
leftAxis.valueFormatter = [[NSNumberFormatter alloc] init];
leftAxis.valueFormatter.maximumFractionDigits = 1;
leftAxis.valueFormatter.negativeSuffix = @"k";
leftAxis.valueFormatter.positiveSuffix = @"k";
leftAxis.valueFormatter.positivePrefix = @"$";
leftAxis.valueFormatter.negativePrefix = @"$";
leftAxis.labelPosition = YAxisLabelPositionOutsideChart;
leftAxis.spaceTop = 0.15;
leftAxis.customAxisMin = 0.0; // this replaces startAtZero = YES


_chartView.legend.position = ChartLegendPositionBelowChartLeft;
_chartView.legend.form = ChartLegendFormSquare;
_chartView.legend.formSize = 20.0;
_chartView.legend.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:20.f];
_chartView.legend.xEntrySpace = 4.0;
_chartView.legend.textColor = [UIColor whiteColor];


NSMutableArray *xVals = [[NSMutableArray alloc] init];

for (int i = 0; i < 12; i++)
{
    [xVals addObject:months[i % 12]];
}

NSMutableArray *yVals = [[NSMutableArray alloc] init];
NSMutableArray *yValsColor = [[NSMutableArray alloc] init];


[yVals addObject:[[BarChartDataEntry alloc] initWithValue:10 xIndex:1]];
[yVals addObject:[[BarChartDataEntry alloc] initWithValue:15 xIndex:2]];
[yVals addObject:[[BarChartDataEntry alloc] initWithValue:20 xIndex:3]];
[yVals addObject:[[BarChartDataEntry alloc] initWithValue:45 xIndex:4]];
[yVals addObject:[[BarChartDataEntry alloc] initWithValue:30 xIndex:5]];
[yVals addObject:[[BarChartDataEntry alloc] initWithValue:5 xIndex:6]];
[yVals addObject:[[BarChartDataEntry alloc] initWithValue:15 xIndex:7]];
[yVals addObject:[[BarChartDataEntry alloc] initWithValue:22 xIndex:8]];
[yVals addObject:[[BarChartDataEntry alloc] initWithValue:32 xIndex:9]];

[yValsColor addObject:[UIColor yellowColor]];

BarChartDataSet *set1 = [[BarChartDataSet alloc] initWithYVals:yVals label:@"Charges"];
set1.barSpace = 0.35;
set1.colors     =  yValsColor;

NSMutableArray *dataSets = [[NSMutableArray alloc] init];
[dataSets addObject:set1];

BarChartData *data = [[BarChartData alloc] initWithXVals:xVals dataSets:dataSets];
[data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:20.f]];
[data setValueTextColor:[UIColor whiteColor]];

_chartView.data = data;


NSArray *arrLabelData = [NSArray arrayWithObjects:@"All", @"1/1/2016 to 9/29/2016", @"All", @"All", @"Date of Billed",nil];
NSArray *arrLabel     = [NSArray arrayWithObjects:@"Ins:",@" DOS:",@" Location:",@" Provider:",@" Aging Calculation Based On:",nil];


NSMutableAttributedString *strFS = [[NSMutableAttributedString alloc] init];
UIColor *color = [UIColor yellowColor]; // select needed color

NSDictionary *attrs = @{ NSForegroundColorAttributeName : color};
NSDictionary *attrsLab = @{ NSForegroundColorAttributeName : [UIColor whiteColor]};

for(int i = 0; i <arrLabelData.count; i++)
{
    NSString *strLabData = [arrLabelData objectAtIndex:i];
    NSString *strLab     = [arrLabel objectAtIndex:i];
    
    NSAttributedString *attStringLab        = [[NSAttributedString alloc] initWithString:strLab attributes:attrsLab];
    NSAttributedString *attStringLabData    = [[NSAttributedString alloc] initWithString:strLabData attributes:attrs];
    
    [strFS appendAttributedString:attStringLab];
    [strFS appendAttributedString:attStringLabData];
    
}
lblAttributes.attributedText = strFS;

I want to respond the data only when user tapped inside the bar.

Any one have idea?

Upvotes: 1

Views: 3064

Answers (5)

Lễ Trần
Lễ Trần

Reputation: 1

  1. Tap to this link: https://github.com/danielgindi/Charts/blob/master/ChartsDemo-iOS/Objective-C/Components/BalloonMarker.swift
  2. Copy all and paste to your project
  3. Add
    myBarChart.delegate = self
    myBarChart.legend.enabled = false
    
  4. Use it
    extension StatisticUsageChartView: ChartViewDelegate {
        func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) {
            let marker: BalloonMarker = BalloonMarker(color: Colors._9e9e9e.color,
                                                      font: UIFont.systemFont(ofSize: 10),
                                                      textColor: .white,
                                                      insets: UIEdgeInsets(top: 5.0, left: 5.0, bottom: 7.0, right: 5.0))
            marker.isPercent = self.isPercent
            marker.minimumSize = CGSizeMake(50, 35)
            chartView.marker = marker
    
            self.myBarChart.centerViewToAnimated(xValue: entry.x, 
                                                 yValue: entry.y,
                                                 axis: self.myBarChart.data![highlight.dataSetIndex].axisDependency,
                                                                 duration: 0.7)
        }
    }
    

Upvotes: 0

ACAkgul
ACAkgul

Reputation: 188

I converted @rohit Sidpara's answer for 3.2.1 version of Charts

Both functions need to be written at BarHighlighter.swift file

open override func getHighlight(x: CGFloat, y: CGFloat) -> Highlight?
{
    guard
        let barData = (self.chart as? BarChartDataProvider)?.barData,
        let high = super.getHighlight(x: x, y: y)
        else { return nil }

    let pos = getValsForTouch(x: x, y: y)

    if let set = barData.getDataSetByIndex(high.dataSetIndex) as? IBarChartDataSet,
        set.isStacked
    {
        return getStackedHighlight(high: high,
                                   set: set,
                                   xValue: Double(pos.x),
                                   yValue: Double(pos.y))
    }
    else
    {
        let set = barData.getDataSetByIndex(high.dataSetIndex) as? IBarChartDataSet
        let entry = set?.entryForIndex(Int(high.x)) as! BarChartDataEntry
        let rectOfData = getBarBounds(e: entry)

        return rectOfData.contains(CGPoint(x: x, y: y)) ? high: nil
    }
}

public func getBarBounds(e: BarChartDataEntry) -> CGRect
{

    guard
        let dataProvider = self.chart as? BarChartDataProvider
        else { return CGRect.null }

    let barspace = CGFloat((dataProvider.barData?.barWidth)!) / 2.0
    let y = CGFloat(e.y)
    let x = CGFloat(e.x)

    let barWidth: CGFloat = 0.5

    let spaceHalf = barspace / 2.0
    let left = x - barWidth + spaceHalf
    let right = x + barWidth - spaceHalf
    let top = y >= 0.0 ? y : 0.0
    let bottom = y <= 0.0 ? y : 0.0

    var bounds = CGRect(x: left, y: top, width: right - left, height: bottom - top)

    dataProvider.getTransformer(forAxis: YAxis.AxisDependency.left).rectValueToPixel(&bounds)

    return bounds
}

Upvotes: 0

user5210683
user5210683

Reputation: 1

Maximum hightlightdistance property is available for chartview default is 500 dp. Change it to 10 and check you're view

Upvotes: -1

rohit Sidpara
rohit Sidpara

Reputation: 547

I have user version 2.2.3 of charts in my App. Try this implementation to resolve your issue

1) Add function to your ChartHighlighter.swift file.

public func getBarBounds(e: BarChartDataEntry) -> CGRect
{

    guard let
        set = self.chart?._data?.getDataSetForEntry(e) as? IBarChartDataSet
        else { return CGRectNull }

    let barspace = set.barSpace
    let y = CGFloat(e.value)
    let x = CGFloat(e.xIndex)

    let barWidth: CGFloat = 0.5

    let spaceHalf = barspace / 2.0
    let left = x - barWidth + spaceHalf
    let right = x + barWidth - spaceHalf
    let top = y >= 0.0 ? y : 0.0
    let bottom = y <= 0.0 ? y : 0.0

    var bounds = CGRect(x: left, y: top, width: right - left, height: bottom - top)

    self.chart?.getTransformer(ChartYAxis.AxisDependency.Left).rectValueToPixel(&bounds)

    return bounds
}

2) Update getHighlight function in ChartHighlighter.swift file.

public func getHighlight(x x: Double, y: Double) -> ChartHighlight?
{
    let xIndex = getXIndex(x)
    if (xIndex == -Int.max)
    {
        return nil
    }


    let dataSetIndex = getDataSetIndex(xIndex: xIndex, x: x, y: y)
    if (dataSetIndex == -Int.max)
    {
        return nil
    }

    //Modification for only Bar Selection
    let dataSet = self.chart!.data!.getDataSetByIndex(dataSetIndex)
    let e = dataSet.entryForXIndex(xIndex) as! BarChartDataEntry!
    let rectData = getBarBounds(e)

    let point = CGPoint(x: x,y: y)
    let isPointInFrame = CGRectContainsPoint(rectData, point)
    if (!isPointInFrame)
    {
        return nil
    }
    //Modification for only Bar Selection


    return ChartHighlight(xIndex: xIndex, dataSetIndex: dataSetIndex)
}

Hope it will help you. Happy Coding...

Upvotes: 2

DevB2F
DevB2F

Reputation: 5095

I have a project with the same library and I was able to get the functionality you are asking for by changing the getHighlight function inside the ChartHighlighter.swift file to the following:

/// Returns a Highlight object corresponding to the given x- and y- touch positions in pixels.
/// - parameter x:
/// - parameter y:
/// - returns:
public func getHighlight(x x: CGFloat, y: CGFloat) -> ChartHighlight?
{
    print("y: \(y)")
    let xIndex = getXIndex(x)

    guard let
        selectionDetail = getSelectionDetail(xIndex: xIndex, y: y, dataSetIndex: nil)
        else { return nil }
    print("selectionDetail: \(selectionDetail.y)")
    if(y<selectionDetail.y) {
        return nil
    }
    else {
        return ChartHighlight(xIndex: xIndex, value: selectionDetail.value, dataIndex: selectionDetail.dataIndex, dataSetIndex: selectionDetail.dataSetIndex, stackIndex: -1)
    }
}

Upvotes: 0

Related Questions