How to create a floating graph marker with ios-charts

|▌冷眼眸甩不掉的悲伤 提交于 2019-12-02 17:42:20

So I finally got this to work and wanted to add it to the community. Here is what my chart looks like:

When you run your finger over the chart the the reader above moves and reads the value from the chart.

To get this to work I had to do the following:

  1. In the file you are implementing the graph, be sure to include ChartViewDelegate

  2. Then implement the chartValueSelected method.

  3. If you then use the getMarkerPosition method, you'll be able to center your "marker" wherever you want.

Here's some sample code:

class MarkerView: UIView {
    @IBOutlet var valueLabel: UILabel!
    @IBOutlet var metricLabel: UILabel!
    @IBOutlet var dateLabel: UILabel!
}

let markerView = MarkerView()

func chartValueSelected(chartView: ChartViewBase, entry: ChartDataEntry, dataSetIndex: Int, highlight: ChartHighlight) { 

    let graphPoint = chartView.getMarkerPosition(entry: entry,  highlight: highlight)

    // Adding top marker
    markerView.valueLabel.text = "\(entry.value)"
    markerView.dateLabel.text = "\(months[entry.xIndex])"
    markerView.center = CGPointMake(graphPoint.x, markerView.center.y)
    markerView.hidden = false

}

markerView is a xib that I added manually to the main view that also contains the graph view. markerView is a UIView that contains three labels. It's the 3.0, impressions, and Apr you see in the picture.

graphPoint is the CGPoint located on the graph. You can use the x and y of the graphPoint to reposition your markerView. Here I simply kept the y value of the markerView the same and changed it's x-value to make it move back and forth on touch.

This is based on the example BallonMarker provided at the iOS-Charts repo.

Hopefully this comes in handy for someone, as I couldn't find other subclass examples of MarkerImage so this was a lot of trail and error for me as never used CGContext before

To use add this class

class PillMarker: MarkerImage {

    private (set) var color: UIColor
    private (set) var font: UIFont
    private (set) var textColor: UIColor
    private var labelText: String = ""
    private var attrs: [NSAttributedString.Key: AnyObject]!

    static let formatter: DateComponentsFormatter = {
        let f = DateComponentsFormatter()
        f.allowedUnits = [.minute, .second]
        f.unitsStyle = .short
        return f
    }()

    init(color: UIColor, font: UIFont, textColor: UIColor) {
        self.color = color
        self.font = font
        self.textColor = textColor

        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.alignment = .center
        attrs = [.font: font, .paragraphStyle: paragraphStyle, .foregroundColor: textColor, .baselineOffset: NSNumber(value: -4)]
        super.init()
    }

    override func draw(context: CGContext, point: CGPoint) {
        // custom padding around text
        let labelWidth = labelText.size(withAttributes: attrs).width + 10
        // if you modify labelHeigh you will have to tweak baselineOffset in attrs
        let labelHeight = labelText.size(withAttributes: attrs).height + 4

        // place pill above the marker, centered along x
        var rectangle = CGRect(x: point.x, y: point.y, width: labelWidth, height: labelHeight)
        rectangle.origin.x -= rectangle.width / 2.0
        let spacing: CGFloat = 20
        rectangle.origin.y -= rectangle.height + spacing

        // rounded rect
        let clipPath = UIBezierPath(roundedRect: rectangle, cornerRadius: 6.0).cgPath
        context.addPath(clipPath)
        context.setFillColor(UIColor.white.cgColor)
        context.setStrokeColor(UIColor.black.cgColor)
        context.closePath()
        context.drawPath(using: .fillStroke)

        // add the text
        labelText.draw(with: rectangle, options: .usesLineFragmentOrigin, attributes: attrs, context: nil)
    }

    override func refreshContent(entry: ChartDataEntry, highlight: Highlight) {
        labelText = customString(entry.y)
    }

    private func customString(_ value: Double) -> String {
        let formattedString = PillMarker.formatter.string(from: TimeInterval(value))!
        // using this to convert the left axis values formatting, ie 2 min
        return "\(formattedString)"
    }
}

Then activate for your chart

let marker = PillMarker(color: .white, font: UIFont.boldSystemFont(ofSize: 14), textColor: .black)
chartView.marker = marker
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!