Flash3001 / iOSCharts.Xamarin

A Xamarin port by Lucas Teixeira for Charts (ios-charts) by Daniel Cohen Gindi, inspired by Philipp Jahoda
Other
68 stars 20 forks source link

Image on xAxis #37

Closed Ruchita-Bora closed 4 years ago

Ruchita-Bora commented 6 years ago

I have plotted bar graph using nuget 3.0.4. Using same I am trying to show images on xaxis instead of labels as shown in image below.

  1. Can you share an example to do so.
  2. Also can you tell me how to show title label for y axis vertically as shown in image

screen shot 2018-05-17 at 11 46 12 am

Ruchita-Bora commented 6 years ago

Hi,

I have found an example in Swift but finding it difficult doing same in xamarin iOS. Need it for my project.. Any help would be appreciated..

Flash3001 commented 6 years ago

@Ruchita-Bora Please share the Swift version you found so we can help you convert it to the Xamarin version.

Ruchita-Bora commented 6 years ago
   func setupLineChart() {

    // Set up the chart properties
    HourlyLineChart.chartDescription = nil
    HourlyLineChart.dragEnabled = true
    HourlyLineChart.scaleYEnabled = false
    HourlyLineChart.scaleXEnabled = true
    HourlyLineChart.pinchZoomEnabled = false
    HourlyLineChart.legend.enabled = false
    HourlyLineChart.doubleTapToZoomEnabled = false
    //HourlyLineChart.zoom(scaleX: 0, scaleY: 0, x: 0, y: 0)
    HourlyLineChart.zoom(scaleX: 4, scaleY: 1, x: 0, y: 0)
    // Make a frame for drawable space with a portoffset with borders
    HourlyLineChart.setViewPortOffsets(left: 0, top: -10, right: 0, bottom: -40)
    HourlyLineChart.animate(xAxisDuration: 1, yAxisDuration: 1, easingOption: .easeInBounce)
    HourlyLineChart.rightAxis.enabled = false
    HourlyLineChart.delegate = self
    HourlyLineChart.noDataFont.withSize(17)

    // Set up expandable view properties
    hourlyExpandableLabel.isHidden = true
    hourlyExpandableView.isHidden = true
    hourlyExpandableView.backgroundColor = UIColor(red: 99/255, green: 140/255, blue: 189/255, alpha: 1/1)
    hourlyExpandableView.layer.cornerRadius = 8
    hourlyExpandableHeight.constant = 0

    // Set Y-Axis properties
    let yAxis = HourlyLineChart.leftAxis
    yAxis.enabled = true
    yAxis.drawAxisLineEnabled = false
    yAxis.drawLabelsEnabled = false
    yAxis.drawGridLinesEnabled = false
    yAxis.spaceTop = 1
    yAxis.spaceBottom = 1.5

    // Set Custom XAXis renderers
    HourlyLineChart.xAxis.enabled = true
    HourlyLineChart.xAxis.drawGridLinesEnabled = false
    HourlyLineChart.xAxis.drawLabelsEnabled = true
    HourlyLineChart.xAxis.labelPosition = .top
    HourlyLineChart.xAxis.labelTextColor = .white
    HourlyLineChart.xAxis.granularityEnabled = true
    HourlyLineChart.xAxis.granularity = 1
    HourlyLineChart.xAxis.avoidFirstLastClippingEnabled = true
    HourlyLineChart.xAxis.labelFont = UIFont(name: "HelveticaNeue-Light", size: 12.0)!

    dataSet.highlightEnabled = true
    dataSet.drawHorizontalHighlightIndicatorEnabled = false
    dataSet.highlightColor = UIColor(red: 236/255, green: 239/255, blue: 241/255, alpha: 0.1/1)
    dataSet.highlightLineWidth = 50
    dataSet.mode = .cubicBezier
    dataSet.circleColors = [UIColor(hex: "fafafa")]
    dataSet.circleRadius = 4
    dataSet.fillColor = UIColor.white
    dataSet.drawCircleHoleEnabled = false
    dataSet.drawFilledEnabled = false
    dataSet.lineWidth = 1.0
    dataSet.colors = [UIColor.white]
    dataSet.drawValuesEnabled = true
    dataSet.valueFont = UIFont(name: "HelveticaNeue-Light", size: 14.0)!
    dataSet.axisDependency = .left
}

Here is the code that updates data

   func updateHourlyData() {
    HourlyLineChart.clear()
    lineChartEntries.removeAll()
    hourlyTimes.removeAll()
    hourlyTemperaturesStr.removeAll()
    hourlyPrecip.removeAll()
    hourlyIcons.removeAll()

  if selectedLocationData?.hourlyFcst?.hourly != nil {
        for index in (selectedLocationData?.hourlyFcst?.hourly)! {
            hourlyTimes.append(index.time!)
            hourlyTemperaturesStr.append((index.temperature?.getValue())!)
            hourlyIcons.append(UIImage(named: "\(index.iconCode!)_main")!)
            hourlyPrecip.append(index.precip!)
        }
    }

    if selectedLocationData?.hourlyFcst?.hourly != nil && hourlyTimes.count > 0 {
        hourlyGraphHeight.constant = 250

        // Populate chartDataEntries
        for index in hourlyTimes.indices {
            let xVal = Double(index).rounded()
            var yVal = Double(index)
            let yValStr = hourlyTemperaturesStr[index]
            if  !yValStr.isEmpty {
                yVal = Double(yValStr)!
            }
            lineChartEntries.append(ChartDataEntry(x: xVal, y: yVal, data: selectedLocationData?.hourlyFcst?.hourly![index]))
        }

        // Set up a dataset for data model
        dataSet.values = lineChartEntries

        // Setup a valueformatter to get rid of decimals in the graph
        let format = NumberFormatter()
        format.numberStyle = .none
        let formatter = HourTempFormatter(numberFormatter: format)
        dataSet.valueFormatter = formatter

        // Set the data using the above dataSet for the chart
        let data = LineChartData(dataSet: dataSet)
        data.setDrawValues(true)
        data.setValueTextColor(UIColor(hex: "fafafa"))

        HourlyLineChart.data = data

        // Custom renederers for icons, precipitation
        let customXAxisRenderer = XAxisCustomRenderer(viewPortHandler: HourlyLineChart.viewPortHandler, xAxis: HourlyLineChart.xAxis, transformer: HourlyLineChart.getTransformer(forAxis: YAxis.AxisDependency.left), icons: hourlyIcons, precipitationArray: hourlyPrecip)
        HourlyLineChart.xAxisRenderer = customXAxisRenderer

        // Custom renderer for time
        HourlyLineChart.xAxis.valueFormatter = IndexAxisValueFormatter(values: hourlyTimes)

        // Backup renderer for times (CHARTS 3.0.4 -> 3.0.5) ISSUE
        //HourlyLineChart.xAxis.valueFormatter = DefaultAxisValueFormatter(block: {(index, _)  in
        //    return self.hourlyTimes[Int(index)]
        //})
    }
    // ELSE BLOCK TO COLLAPSE CHART IF NO DATA
    //else {
   //            hourlyGraphHeight.constant = 0
   //        }
    HourlyLineChart.notifyDataSetChanged()
}

And here is the custom xAxisRenderer:

public class XAxisCustomRenderer: XAxisRenderer {
// Dict to hold the weather icons array
private var icons: [UIImage]
// Dict to hold the preciptation mapping
private var precipitationArray: [String]
// Array to hold the time string
//private var timesArray: [String]

init(viewPortHandler: ViewPortHandler, xAxis: XAxis, transformer: Transformer, icons: [UIImage], precipitationArray: [String]) {

    self.icons = icons
    self.precipitationArray = precipitationArray
    //self.timesArray = times

    super.init(viewPortHandler: viewPortHandler, xAxis: xAxis, transformer: transformer)
}

override public func drawLabels(context: CGContext, pos: CGFloat, anchor: CGPoint){
    guard
        let xAxis = self.axis as? XAxis,
        let transformer = self.transformer
        else { return }

    #if os(OSX)
        let paraStyle = NSParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
    #else
        let paraStyle = NSParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
    #endif
    paraStyle.alignment = .center

    let labelAttrs: [NSAttributedStringKey : Any] = [NSAttributedStringKey.font: xAxis.labelFont,
                                                     NSAttributedStringKey.foregroundColor: xAxis.labelTextColor,
                                                     NSAttributedStringKey.paragraphStyle: paraStyle]
    let FDEG2RAD = CGFloat(Double.pi / 180.0)

    let labelRotationAngleRadians = xAxis.labelRotationAngle * FDEG2RAD

    let centeringEnabled = xAxis.isCenterAxisLabelsEnabled

    let valueToPixelMatrix = transformer.valueToPixelMatrix

    var position = CGPoint(x: 0.0, y: 0.0)

    var labelMaxSize = CGSize()

    if xAxis.isWordWrapEnabled
    {
        labelMaxSize.width = xAxis.wordWrapWidthPercent * valueToPixelMatrix.a
    }

    let entries = xAxis.entries

    for i in stride(from: 0, to: entries.count, by: 1)
    {
        if centeringEnabled
        {
            position.x = CGFloat(xAxis.centeredEntries[i])
        }
        else
        {
            position.x = CGFloat(entries[i])
        }

        position.y = 0.0
        position = position.applying(valueToPixelMatrix)

        if viewPortHandler.isInBoundsX(position.x)
        {
            let label = xAxis.valueFormatter?.stringForValue(xAxis.entries[i], axis: xAxis) ?? ""

            let labelns = label as NSString

            if xAxis.isAvoidFirstLastClippingEnabled
            {
                // avoid clipping of the last
                if i == xAxis.entryCount - 1 && xAxis.entryCount > 1
                {
                    let width = labelns.boundingRect(with: labelMaxSize, options: .usesLineFragmentOrigin, attributes: labelAttrs, context: nil).size.width

                    if width > (viewPortHandler.offsetRight) * 2.0
                        && position.x + width > viewPortHandler.chartWidth
                    {
                        position.x -= width / 2.0
                    }
                }
                else if i == 0
                { // avoid clipping of the first
                    let width = labelns.boundingRect(with: labelMaxSize, options: .usesLineFragmentOrigin, attributes: labelAttrs, context: nil).size.width
                    position.x += width / 2.0
                }
            }

            let rawIcon: UIImage = icons[Int(entries[i])].fixedOrientation().imageRotatedByDegrees(degrees: 180)
            let icon: CGImage = rawIcon.cgImage!

            // Draw the time labels
            drawLabel(
                context: context,
                formattedLabel: label,
                x: position.x,
                y: pos + 33,
                attributes: labelAttrs,
                constrainedToSize: labelMaxSize,
                anchor: anchor,
                angleRadians: labelRotationAngleRadians)

          //                let time = timesArray[Int(i)]

            //drawLabel(context: context, formattedLabel: time, x: position.x + 8, y: viewPortHandler.chartHeight - 2, attributes: labelAttrs, constrainedToSize: labelMaxSize, anchor: anchor, angleRadians: labelRotationAngleRadians)

            // Draw the icons
            context.draw(icon, in: CGRect(x: position.x - 15, y: viewPortHandler.chartHeight - 47, width: CGFloat(30), height: CGFloat(27)))

            // Draw the precipitation value
            let precip = precipitationArray[Int(i)]
            //if( !(precip.starts(with: "0"))) {

            // Draw the precip string
            drawLabel(context: context, formattedLabel: precip, x: position.x + 8, y: viewPortHandler.chartHeight - 2, attributes: labelAttrs, constrainedToSize: labelMaxSize, anchor: anchor, angleRadians: labelRotationAngleRadians)

            // Draw the precip image
            let precipitationIcon: CGImage = (UIImage(named: "rainCloud")?.fixedOrientation().imageRotatedByDegrees(degrees: 180).cgImage)!
            context.draw(precipitationIcon, in: CGRect(x: position.x - 19, y: viewPortHandler.chartHeight - 15, width: 15, height: 15))

            let axisLine: CGImage = (UIImage(named: "axisLine")?.cgImage)!

            // Draw top axis
            context.draw(axisLine,in: CGRect(x: -20, y: 0, width: viewPortHandler.chartWidth + 50, height: 50))

            //let arrowIconRight: CGImage = (UIImage(named: "arrow")?.fixedOrientation().imageRotatedByDegrees(degrees: 180).alpha(alpha: 0.2)!.cgImage)!
            //let arrowIconLeft: CGImage = (UIImage(named: "arrow")?.fixedOrientation().imageRotatedByDegrees(degrees: 0).alpha(alpha: 0.2)!.cgImage)!
            //context.draw(arrowIconRight, in: CGRect(x: viewPortHandler.chartWidth - 30 , y: viewPortHandler.chartHeight/2.5, width: 15, height: 15))
            //context.draw(arrowIconLeft, in: CGRect(x: 10 , y: viewPortHandler.chartHeight/2.5, width: 15, height: 15))

            // Draw bottom axis
            context.draw(axisLine,in: CGRect(x: -20 , y: viewPortHandler.chartHeight - 80, width: viewPortHandler.chartWidth + 50, height: 50))
        }
    }
}

}