如何在iOS图表上添加标记?

4
我正在使用Objective-C创建一个iOS应用程序,需要使用ios-charts。
现在我面临的问题是找不到向我的图表视图添加标记的方法。
此外,我需要通过用户操作更改Y轴数据集,但我不知道如何实现此功能。
谢谢您的帮助。
5个回答

15

很不幸,在库本身中没有显示带有文本标记的类。在github提供的示例中有BalloonMarker类,但它未包含在库中。因此,您可以使用来自github示例的BalloonMarker,或者以下是另一种简单的标记类,您可以使用它代替。我认为它可能比BallonMarker更容易理解和定制:

class ChartMarker: MarkerView {
    var text = ""

    override func refreshContent(entry: ChartDataEntry, highlight: Highlight) {
        super.refreshContent(entry: entry, highlight: highlight)
        text = String(entry.y)
    }

    override func draw(context: CGContext, point: CGPoint) {
        super.draw(context: context, point: point)

        var drawAttributes = [NSAttributedStringKey : Any]()
        drawAttributes[.font] = UIFont.systemFont(ofSize: 15)
        drawAttributes[.foregroundColor] = UIColor.white
        drawAttributes[.backgroundColor] = UIColor.darkGray

        self.bounds.size = (" \(text) " as NSString).size(withAttributes: drawAttributes)
        self.offset = CGPoint(x: 0, y: -self.bounds.size.height - 2)

        let offset = self.offsetForDrawing(atPoint: point)

        drawText(text: " \(text) " as NSString, rect: CGRect(origin: CGPoint(x: point.x + offset.x, y: point.y + offset.y), size: self.bounds.size), withAttributes: drawAttributes)
    }

    func drawText(text: NSString, rect: CGRect, withAttributes attributes: [NSAttributedStringKey : Any]? = nil) {
        let size = text.size(withAttributes: attributes)
        let centeredRect = CGRect(x: rect.origin.x + (rect.size.width - size.width) / 2.0, y: rect.origin.y + (rect.size.height - size.height) / 2.0, width: size.width, height: size.height)
        text.draw(in: centeredRect, withAttributes: attributes)
    }
}

并将其与给定的图表一起使用:

let marker = ChartMarker()
marker.chartView = chartView
chartView.marker = marker

怎样添加文本?尝试过以下两种方法:marker.drawText(text: "hello world!", rect: CGRect(x: 50, y: 50, width: 100, height: 100)) 和 marker.text = "hello world!" - BriOnH
在上面的例子中,y值的文本应该自动绘制,因为refreshContent方法将被自动调用,并且在此方法中,它将保存值到文本变量中,稍后当draw方法被自动调用时将使用该值。如果您想要添加一些前缀或类似的内容,应该在refreshContent方法中完成。 - Leszek Szary

6

将以下代码和 BalloonMarker 类添加到 Charts->Components 文件夹中:

var charts = LineChartView()

let marker:BalloonMarker = BalloonMarker(color: UIColor(red: 93/255, green: 186/255, blue: 215/255, alpha: 1), font: UIFont(name: "Helvetica", size: 12)!, textColor: UIColor.white, insets: UIEdgeInsets(top: 7.0, left: 7.0, bottom: 25.0, right: 7.0))
 marker.minimumSize = CGSize(width: 75.0, height: 35.0)//CGSize(75.0, 35.0)
charts.marker = marker

//气球标记类

import Foundation
import CoreGraphics
import UIKit


open class BalloonMarker: MarkerImage
{
    open var color: UIColor?
    open var arrowSize = CGSize(width: 15, height: 11)
    open var font: UIFont?
    open var textColor: UIColor?
    open var insets = UIEdgeInsets()
    open var minimumSize = CGSize()

    fileprivate var label: String?
    fileprivate var _labelSize: CGSize = CGSize()
    fileprivate var _paragraphStyle: NSMutableParagraphStyle?
    fileprivate var _drawAttributes = [String : AnyObject]()

    public init(color: UIColor, font: UIFont, textColor: UIColor, insets: UIEdgeInsets)
    {
        super.init()

        self.color = color
        self.font = font
        self.textColor = textColor
        self.insets = insets

        _paragraphStyle = NSParagraphStyle.default.mutableCopy() as? NSMutableParagraphStyle
        _paragraphStyle?.alignment = .center
    }

    open override func offsetForDrawing(atPoint point: CGPoint) -> CGPoint
    {
        let size = self.size
        var point = point
        point.x -= size.width / 2.0
        point.y -= size.height
        return super.offsetForDrawing(atPoint: point)
    }

    open override func draw(context: CGContext, point: CGPoint)
    {
        guard let label = label else { return }

        let offset = self.offsetForDrawing(atPoint: point)
        let size = self.size

        var rect = CGRect(
            origin: CGPoint(
                x: point.x + offset.x,
                y: point.y + offset.y),
            size: size)
        rect.origin.x -= size.width / 2.0
        rect.origin.y -= size.height

        context.saveGState()

        if let color = color
        {
            context.setFillColor(color.cgColor)
            context.beginPath()
            context.move(to: CGPoint(
                x: rect.origin.x,
                y: rect.origin.y))
            context.addLine(to: CGPoint(
                x: rect.origin.x + rect.size.width,
                y: rect.origin.y))
            context.addLine(to: CGPoint(
                x: rect.origin.x + rect.size.width,
                y: rect.origin.y + rect.size.height - arrowSize.height))
            context.addLine(to: CGPoint(
                x: rect.origin.x + (rect.size.width + arrowSize.width) / 2.0,
                y: rect.origin.y + rect.size.height - arrowSize.height))
            context.addLine(to: CGPoint(
                x: rect.origin.x + rect.size.width / 2.0,
                y: rect.origin.y + rect.size.height))
            context.addLine(to: CGPoint(
                x: rect.origin.x + (rect.size.width - arrowSize.width) / 2.0,
                y: rect.origin.y + rect.size.height - arrowSize.height))
            context.addLine(to: CGPoint(
                x: rect.origin.x,
                y: rect.origin.y + rect.size.height - arrowSize.height))
            context.addLine(to: CGPoint(
                x: rect.origin.x,
                y: rect.origin.y))
            context.fillPath()
        }

        rect.origin.y += self.insets.top
        rect.size.height -= self.insets.top + self.insets.bottom

        UIGraphicsPushContext(context)

        label.draw(in: rect, withAttributes: _drawAttributes)

        UIGraphicsPopContext()

        context.restoreGState()
    }

    open override func refreshContent(entry: ChartDataEntry, highlight: Highlight)
    {
        setLabel(String(entry.y))
    }

    open func setLabel(_ newLabel: String)
    {
        label = newLabel

        _drawAttributes.removeAll()
        _drawAttributes[NSFontAttributeName] = self.font
        _drawAttributes[NSParagraphStyleAttributeName] = _paragraphStyle
        _drawAttributes[NSForegroundColorAttributeName] = self.textColor

        _labelSize = label?.size(attributes: _drawAttributes) ?? CGSize.zero

        var size = CGSize()
        size.width = _labelSize.width + self.insets.left + self.insets.right
        size.height = _labelSize.height + self.insets.top + self.insets.bottom
        size.width = max(minimumSize.width, size.width)
        size.height = max(minimumSize.height, size.height)
        self.size = size
    }
}

将此 BalloonMarker 类添加到 Charts->components 文件夹中。 - Malleswari
2
这个库为什么没有包含这个功能呢? - Richard Witherspoon
有关 https://stackoverflow.com/questions/62343054/how-to-manually-add-marker-in-swift-charts-at-fixed-point 的任何想法吗? - Amit

4

基于这篇答案,介绍了ChartMarker类。我已将其更新到Swift 5并进行了清理,减少了不必要的分配,提高了可读性。

class ChartMarker: MarkerView {
    private var text = String()

    private let drawAttributes: [NSAttributedString.Key: Any] = [
        .font: UIFont.systemFont(ofSize: 15),
        .foregroundColor: UIColor.white,
        .backgroundColor: UIColor.darkGray
    ]

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

    override func draw(context: CGContext, point: CGPoint) {
        super.draw(context: context, point: point)

        let sizeForDrawing = text.size(withAttributes: drawAttributes)
        bounds.size = sizeForDrawing
        offset = CGPoint(x: -sizeForDrawing.width / 2, y: -sizeForDrawing.height - 4)

        let offset = offsetForDrawing(atPoint: point)
        let originPoint = CGPoint(x: point.x + offset.x, y: point.y + offset.y)
        let rectForText = CGRect(origin: originPoint, size: sizeForDrawing)
        drawText(text: text, rect: rectForText, withAttributes: drawAttributes)
    }

    private func drawText(text: String, rect: CGRect, withAttributes attributes: [NSAttributedString.Key: Any]? = nil) {
        let size = bounds.size
        let centeredRect = CGRect(
            x: rect.origin.x + (rect.size.width - size.width) / 2,
            y: rect.origin.y + (rect.size.height - size.height) / 2,
            width: size.width,
            height: size.height
        )
        text.draw(in: centeredRect, withAttributes: attributes)
    }
}

使用方法:

let marker = ChartMarker()
marker.chartView = chartView
chartView.marker = marker

如何增加标签的高度并应用cornerRadius? - MilanPanchal
嗨。当我在图表上点击某个点时,什么也不会发生。refreshContent方法没有被调用。我应该检查什么?chartView.drawMarkers = true并且set.highlightEnabled = true。 - revolutionkpi

1
首先,一次只提出一个问题。不要在一个帖子中问两个不相关的问题。
ios-charts有ChartMarker类来添加自定义标记。
ChartsDemo中有演示代码:
BalloonMarker *marker = [[BalloonMarker alloc] initWithColor:[UIColor colorWithWhite:180/255. alpha:1.0] font:[UIFont systemFontOfSize:12.0] insets: UIEdgeInsetsMake(8.0, 8.0, 20.0, 8.0)];
marker.minimumSize = CGSizeMake(80.f, 40.f);
_chartView.marker = marker;

你也可以自己编写标记。看一下代码,它还可以绘制图像而不是文本。
如果你想改变数据条目,只需通过addEntry添加新条目到数据集并调用notifyDataSetChanged,或者重新创建一个新的chartData(可能会有性能问题)。
你真的应该先尝试在GitHub页面或SO上搜索旧问题,因为你的问题只是重复的。

谢谢您的建议。很抱歉在一篇帖子中写了两个问题。对于重复的问题我感到非常抱歉。我尝试过搜索,但是没有找到相关的问题。由于我现在病在床上,明天我会尽力编写您提供的代码。 - Masaru Kitajima
我添加了您上面友好提供的代码。但是 Xcode 显示“使用未声明的标识符 'BalloonMarker'”。我使用 cocoapods 添加了 ios-charts,并构建了框架并将其链接到我的项目。有人告诉我,当使用 cocopods Charts 时,只需要“@import Charts”,不需要#import任何头文件。请问您能告诉我如何解决这个问题吗?谢谢您的帮助。 - Masaru Kitajima
伙计,气球标记器只是ChartsDemo中的一个示例类,而不是Charts中的类。你能否先学会阅读代码并与ChartsDemo进行实验?你需要的是子类ChartMarker来创建自己的标记器。气球标记器只是你可以参考的示例。 - Wingzero
谢谢你的建议。我正在尝试阅读代码,但我只是Swift的初学者,这让我很难理解。非常抱歉打扰你,同时感谢你抽出宝贵时间帮我。 - Masaru Kitajima
1
有人能提供一个Swift3的例子吗?谢谢。 - Lysdexia
显示剩余2条评论

1
你只需要实现IChartMarker或从演示中导入swift类即可。
我使用的是objective-c项目,为了保证正常,我在objective-c中实现了IChartMarker,并将其放在gist上。我将其放在gist上供将来遇到此问题的人使用。它相当基础,所以您可能想要修改它。

https://gist.github.com/jakehoskins/8d668688f62fa93ecf8bf7921428c53f


1
非常感谢!你的要点非常有用,帮了我很多。再次感谢! - Masaru Kitajima

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接