Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chart Selected function is not called on click of every Bar. #2291

Closed
gsolanki1509 opened this issue Mar 28, 2017 · 20 comments
Closed

Chart Selected function is not called on click of every Bar. #2291

gsolanki1509 opened this issue Mar 28, 2017 · 20 comments

Comments

@gsolanki1509
Copy link

I'm using Swift 3.0 charts. And to get click event i'm calling below function:

func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) {
print(entry.description)
}

But above function is not called on evry Bar i clicked. It is called only on one Bar. When i tried to click on rest of the Bars other than one below function is called.

func chartValueNothingSelected(_ chartView: ChartViewBase) {
print("chartValueNothingSelected")
}

Please suggest me somethng..Thanks in advance..!!

@thierryH91200
Copy link
Contributor

add
chartView.delegate = self

@gsolanki1509
Copy link
Author

i already did that.

The problem is its is been called only for one bar not for all.

@thierryH91200
Copy link
Contributor

// MARK: - ChartViewDelegate
extension yourController: ChartViewDelegate
{
    public func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight)
    {
        print("chartValueSelected : x = \(highlight.x)")
    }
    
    public func chartValueNothingSelected(_ chartView: ChartViewBase)
    {
        print("chartValueNothingSelected")
    }
}

@gsolanki1509
Copy link
Author

Thanks for your reply. I tried this also. But still it is called on that particular one bar only.

screen shot 2017-03-28 at 11 01 12 am

Please find my code as below:

i

import UIKit
import Charts

class ViewController: UIViewController, ChartViewDelegate {

@IBOutlet weak var barChart: BarChartView!

var values : [String]! = []
override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    
    
    barChart.delegate = self
    barChart.descriptionText = ""
//    barChart.maxVisibleCount = 60
    barChart.pinchZoomEnabled = false
    barChart.drawBarShadowEnabled = false
    barChart.doubleTapToZoomEnabled = false
    barChart.drawGridBackgroundEnabled = false
    var xAxis: XAxis? = barChart.xAxis
    xAxis?.labelPosition = .bottom
    xAxis?.drawGridLinesEnabled = false
    barChart.leftAxis.drawGridLinesEnabled = false
    barChart.rightAxis.drawGridLinesEnabled = false
    barChart.legend.enabled = false
    
    barChart.leftAxis.axisMinimum = 0.0//min(0.0, barChart.data!.yMin - 1.0)
    barChart.leftAxis.axisMaximum = 150.0//max(10.0, barChart.data!.yMax + 1.0)
    barChart.leftAxis.labelCount = 6//Int(barChart.leftAxis.axisMinimum - barChart.leftAxis.axisMaximum)
    barChart.leftAxis.drawZeroLineEnabled = false
    
    let months = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
    
    values = ["30.0", "45.0", "135.2", "75.5", "90.5", "115.5", "85.5"]
    
    barChart.xAxis.valueFormatter = IndexAxisValueFormatter(values:months)
    barChart.xAxis.granularity = 1
    
    self.updateChartData()
    
}


func updateChartData(){
    
    setDataCount(7, range: 100.0)
}

func setDataCount(_ count: Int, range: Double) {
    var yVals = [Any]()
    //   for (int i = 0; i < count; i++) {
    for var i in (0..<count).reversed() {
        let mult: Double = (range + 1)
        let val = Double((values?[i])!)//Double(arc4random_uniform(UInt32(mult))) + mult / 3.0
        yVals.append(BarChartDataEntry(x: Double(i), y: val!))
        print(yVals.count)
        
    }
    var set1: BarChartDataSet? = nil
    print(barChart.data)
    if barChart.data == nil {
        //set1 = BarChartDataSet(values: yVals, label: "DataSet")
        set1 = BarChartDataSet(values: yVals as? [ChartDataEntry], label: "DataSet")
        set1?.colors = ChartColorTemplates.vordiplom()
        set1?.drawValuesEnabled = false
        var dataSets = [ChartDataSet]()
        dataSets.append(set1!)
        let data = BarChartData(dataSets: dataSets)
        
        
        let marker : BalloonMarker = BalloonMarker(color: UIColor.black, font: UIFont(name: "Helvetica", size: 12)!, textColor: UIColor.white, insets: UIEdgeInsets(top: 7.0, left: 7.0, bottom: 7.0, right: 7.0))
        marker.minimumSize = CGSize(width: CGFloat(80.0), height: CGFloat(40.0))
        barChart.marker = marker
        barChart.data = data
        barChart.fitBars = true
    }
    else {
        set1 = (barChart.data?.dataSets[0] as? BarChartDataSet)
        set1?.values = yVals as! [ChartDataEntry]
        barChart.data?.notifyDataChanged()
        barChart.notifyDataSetChanged()
        
    }
    barChart.setNeedsDisplay()
}
override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}


// MARK: - ChartViewDelegate

func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) {
    print("chartValueSelected")
    print(entry.description)
}


func chartValueNothingSelected(_ chartView: ChartViewBase) {
    print("chartValueNothingSelected")
}

}

@thierryH91200
Copy link
Contributor

add extension yourController: ChartViewDelegate

// MARK: - ChartViewDelegate
extension yourController: ChartViewDelegate
{
public func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight)
{
print("chartValueSelected : x = (highlight.x)")
}

public func chartValueNothingSelected(_ chartView: ChartViewBase)
{
    print("chartValueNothingSelected")
}

}

@gsolanki1509
Copy link
Author

Yeah i tried that also. But same issue :/

import UIKit
import Charts

// MARK: - ChartViewDelegate
extension ViewController: ChartViewDelegate
{
public func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight)
{
print("chartValueSelected : x = (highlight.x)")
}

public func chartValueNothingSelected(_ chartView: ChartViewBase)
{
    print("chartValueNothingSelected")
}

}

class ViewController: UIViewController {

@IBOutlet weak var barChart: BarChartView!
var values : [String]! = []

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.

barChart.delegate = self
barChart.descriptionText = ""

// barChart.maxVisibleCount = 60
barChart.pinchZoomEnabled = false
barChart.drawBarShadowEnabled = false
barChart.doubleTapToZoomEnabled = false
barChart.drawGridBackgroundEnabled = false
var xAxis: XAxis? = barChart.xAxis
xAxis?.labelPosition = .bottom
xAxis?.drawGridLinesEnabled = false
barChart.leftAxis.drawGridLinesEnabled = false
barChart.rightAxis.drawGridLinesEnabled = false
barChart.legend.enabled = false

barChart.leftAxis.axisMinimum = 0.0//min(0.0, barChart.data!.yMin - 1.0)
barChart.leftAxis.axisMaximum = 150.0//max(10.0, barChart.data!.yMax + 1.0)
barChart.leftAxis.labelCount = 6//Int(barChart.leftAxis.axisMinimum - barChart.leftAxis.axisMaximum)
barChart.leftAxis.drawZeroLineEnabled = false

let months = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]

values = ["30.0", "45.0", "135.2", "75.5", "90.5", "115.5", "85.5"]

barChart.xAxis.valueFormatter = IndexAxisValueFormatter(values:months)
barChart.xAxis.granularity = 1

self.updateChartData()

}

func updateChartData(){

setDataCount(7, range: 100.0)

}

func setDataCount(_ count: Int, range: Double) {
var yVals = Any
// for (int i = 0; i < count; i++) {
for var i in (0..<count).reversed() {
let mult: Double = (range + 1)
let val = Double((values?[i])!)//Double(arc4random_uniform(UInt32(mult))) + mult / 3.0
yVals.append(BarChartDataEntry(x: Double(i), y: val!))
print(yVals.count)

}
var set1: BarChartDataSet? = nil
print(barChart.data)
if barChart.data == nil {
    //set1 = BarChartDataSet(values: yVals, label: "DataSet")
    set1 = BarChartDataSet(values: yVals as? [ChartDataEntry], label: "DataSet")
    set1?.colors = ChartColorTemplates.vordiplom()
    set1?.drawValuesEnabled = false
    var dataSets = [ChartDataSet]()
    dataSets.append(set1!)
    let data = BarChartData(dataSets: dataSets)
    
    
    let marker : BalloonMarker = BalloonMarker(color: UIColor.black, font: UIFont(name: "Helvetica", size: 12)!, textColor: UIColor.white, insets: UIEdgeInsets(top: 7.0, left: 7.0, bottom: 7.0, right: 7.0))
    marker.minimumSize = CGSize(width: CGFloat(80.0), height: CGFloat(40.0))
    barChart.marker = marker
    barChart.data = data
    barChart.fitBars = true
}
else {
    set1 = (barChart.data?.dataSets[0] as? BarChartDataSet)
    set1?.values = yVals as! [ChartDataEntry]
    barChart.data?.notifyDataChanged()
    barChart.notifyDataSetChanged()
    
}
barChart.setNeedsDisplay()

}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

}

Only for one Bar that selected function is called and for rest Notselected function is called. Don't know why??

@gsolanki1509
Copy link
Author

I tried changing the XAxis counts from 7 to 4. But still it will get call on only one bar.

@thierryH91200
Copy link
Contributor

clean your code
remove ?
var yVals = Any => var yVals = ChartDataEntry

@gsolanki1509
Copy link
Author

Tried all points.

But still same.

Do you want my sample project? So that you can figure it out more.

@thierryH91200
Copy link
Contributor

for i in 0..<count //).reversed()

this is the problem

and it is ok

func setDataCount(_ count: Int, range: Double)
{
var yVals = BarChartDataEntry
for i in 0..<count //).reversed()
{
let val = Double((values[i]))
yVals.append(BarChartDataEntry(x: Double(i), y: val!))
}

    var set1 = BarChartDataSet()
    
    if barChartView.data == nil
    {
        set1 = BarChartDataSet(values: yVals, label: "DataSet")
        set1.colors = ChartColorTemplates.vordiplom()
        set1.drawValuesEnabled = false
        var dataSets = [ChartDataSet]()
        dataSets.append(set1)
        let data = BarChartData(dataSets: dataSets)
        
        
        let marker : BalloonMarker = BalloonMarker(color: NSUIColor.black, font: NSUIFont(name: "Helvetica", size: 12)!, textColor: NSUIColor.white, insets: EdgeInsets(top: 7.0, left: 7.0, bottom: 7.0, right: 7.0))
        marker.minimumSize = CGSize(width: CGFloat(80.0), height: CGFloat(40.0))
        barChartView.marker = marker
        
        barChartView.data = data
        barChartView.fitBars = true
    }
    else
    {
        set1 = (barChartView.data!.dataSets[0] as! BarChartDataSet )
        set1.values = yVals
        
        barChartView.data?.notifyDataChanged()
        barChartView.notifyDataSetChanged()
        
    }
}

@thierryH91200
Copy link
Contributor

The values of x must be in ascending order

@gsolanki1509
Copy link
Author

woahoo.. awesome man..!! It worked.. thanks a lot.. 👍

And can i change the value of Selected Bar. Currently, the values are shown in marker.
But i want to show it in Hr and mins. Is it possible?

Cuz i ddnt find anything where i can manipulate selected bar values before it popped up in marker.

@thierryH91200
Copy link
Contributor

thierryH91200 commented Mar 28, 2017

use XYMarkerView.swift

and

   let  marker = XYMarkerView( color: #colorLiteral(red: 0.921431005, green: 0.9214526415, blue: 0.9214410186, alpha: 1), font: NSFont.systemFont(ofSize: 12.0),
                                textColor: NSColor.blue,
                                insets: NSEdgeInsetsMake(8.0, 8.0, 20.0, 8.0),
                                xAxisValueFormatter: DateValueFormatter(),
                                yAxisValueFormatter: DoubleAxisValueFormatter())

capture d ecran 2017-03-28 a 11 44 56

@gsolanki1509
Copy link
Author

No, actually i don't dates to be on X-Axis. For info, please check my desired output.

screen shot 2017-03-28 at 1 57 18 pm

So, to show Sun- Sat im taking in one array i.e fine:
let months = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]

But to show time i have first converting to minutes and then passing as an data into chart.like below:

    values = ["50.0", "25.0", "135.2", "75.5", "90.5", "115.5", "85.5"]

But once user click on particular Bar, i need to convert above values from minutes to hr-min shows in screenshot. So, for that i have to manipulate data. But i don't see any option for it.

@dattatrayParthenon
Copy link

dattatrayParthenon commented Dec 8, 2021

The values of x must be in ascending order

In may case x values are in ascending order still bar are not tappable. below is the function which returns data.

@dattatrayParthenon
Copy link

dattatrayParthenon commented Dec 8, 2021

    public class func getBarChartData(entries:[[AMChartDataEntry]], axisDependency:YAxis.AxisDependency = .right, color:UIColor = .amTurquoiseBlue, barWidth:Double = 0.4)->BarChartData?{
        var dataSets = [BarChartDataSet]()
        for yValues in entries{
            let yVals = yValues.map({BarChartDataEntry(x: $0.x, y: $0.y, data: $0.data)}).sorted(by: {$0.x < $1.x})
            let dataSet = BarChartDataSet(entries: yVals)
            dataSet.axisDependency = axisDependency
            dataSet.setColor(color)
            dataSets.append(dataSet)
        }
        if dataSets.isEmpty {
            return nil
        }
        let data = BarChartData(dataSets: dataSets)
        data.barWidth = barWidth
        return data
    }

@Lebron1992
Copy link

Lebron1992 commented Mar 26, 2022

@danielgindi I have the same issue and found the bug.

The bug is in the method in ChartDataSet.swift:

open override func entriesForXValue(_ xValue: Double) -> [ChartDataEntry]
    {
        let match: (ChartDataEntry) -> Bool = { $0.x == xValue }
        let i = partitioningIndex(where: match)
        guard i < endIndex else { return [] }
        return self[i...].prefix(while: match)
    }

For example:

  1. assume we have xValues: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
  2. when the user taps the bar at index 10, it means xValue is 11.
  3. The method entriesForXValue should return the entry at index 10. But it returns an empty array.

To fix the bug, my temporary solution is to custom the BarChartDataSet as follow:

import Charts

final class CustomBarChartDataSet: BarChartDataSet {
    override func entriesForXValue(_ xValue: Double) -> [ChartDataEntry] {
        entries.filter { $0.x == xValue }
    }
}

In addition, to make the clicks more accurate, we need to override the method in CustomBarChartDataSet:

override func entryIndex(x xValue: Double, closestToY yValue: Double, rounding: ChartDataSetRounding) -> Int {
    // correct the `closest`
    var closest = -1
    let distancesToXvalue = entries.map { abs(xValue - $0.x) }
    if let min = distancesToXvalue.min() {
        closest = distancesToXvalue.firstIndex(of: min) ?? -1
    }

    guard closest < endIndex else { return -1 }

    let closestXValue = self[closest].x

    switch rounding {
    case .up:
        // If rounding up, and found x-value is lower than specified x, and we can go upper...
        if closestXValue < xValue && closest < index(before: endIndex) {
            formIndex(after: &closest)
        }

    case .down:
        // If rounding down, and found x-value is upper than specified x, and we can go lower...
        if closestXValue > xValue && closest > startIndex {
            formIndex(before: &closest)
        }

    case .closest:
        break
    }

    // Search by closest to y-value
    if !yValue.isNaN {
        while closest > startIndex && self[index(before: closest)].x == closestXValue {
            formIndex(before: &closest)
        }

        var closestYValue = self[closest].y
        var closestYIndex = closest

        while closest < index(before: endIndex) {
            formIndex(after: &closest)
            let value = self[closest]

            if value.x != closestXValue { break }
            if abs(value.y - yValue) <= abs(closestYValue - yValue) {
                closestYValue = yValue
                closestYIndex = closest
            }
        }

        closest = closestYIndex
    }

    return closest
}

@iphone201988
Copy link

@Lebron1992 , Man you are a life saver , Thank you very much for the solution.

@Lebron1992
Copy link

@Lebron1992 , Man you are a life saver , Thank you very much for the solution.

@iphone201988 You are welcome!

@CavalcanteLeo
Copy link

@Lebron1992 maaaaaannnnnnnnnnnnnnnnnn, i love you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants