I\'m using the ios-charts framework and want to create a marker that floats over the graph when I touch and move my finger from side to side. I\'m using a line chart just for r
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