使用 iOS 个图表进行交互式跟踪。如何

Interactive Tracking with iOS Charts. How to

使用 iOS 图表(作者 Daniel Gindi https://github.com/danielgindi/Charts),是否可以跟踪触摸?因此,当我在屏幕上移动手指时,它不会平移图形,而是会持续突出显示我触摸的条目。像下图这样的东西。

我正在考虑向图表视图添加一个 UIGestureRecognizer,获取触摸的位置,并以编程方式突出显示条目。但是我怎样才能得到那个特定位置的条目呢?谢谢

我想出了一个办法来拯救你

extension GraphViewController: ChartViewDelegate {

    func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) {

        // I used the x pixel property of the highlight to update the
        // position of the floating label, and set its text to the 
        // x value of the selected entry
        floatingLabelCenterConstraint?.constant = highlight.xPx
        floatingLabel.text = "  Mar 14  \(highlight.x)"

        // Create the nice transition animation
        // fadeIn() and fadeOut() are simple extension methods I wrote
        if floatingLabel.isHidden {
            floatingLabel.fadeIn()
            subscripts.fadeOut()
        }

        // This gives you the y value of the selected entry
        greenNumber.text = NSString(format: "%.2f", highlight.y) as String
    }
}

但是,只有在您移动手指时才会调用此委托方法。您需要一种方法来判断平移手势何时结束。 Charts 没有开箱即用的委托方法,因此您需要添加它。转到 ChartViewBase.swift 源文件,将以下内容添加到 ChartViewDelegate 协议

public protocol ChartViewDelegate
{
    ...
    @objc optional func panGestureEnded(_ chartView: ChartViewBase)
}

然后转到BarLineChartViewBase.swift,找到panGestureRecognized函数

@objc private func panGestureRecognized(_ recognizer: NSUIPanGestureRecognizer)
{
    ...
    else if recognizer.state == NSUIGestureRecognizerState.ended || recognizer.state == NSUIGestureRecognizerState.cancelled
    {
    ...

    // Add this line at the end
    delegate?.panGestureEnded?(self)
    }
}

最后,返回您的 viewController 并添加以下内容。现在一切都很顺利

func panGestureEnded(_ chartView: ChartViewBase) {
    subscripts.fadeIn()
    floatingLabel.fadeOut()

    // clear selection by setting highlightValue to nil
    chartView.highlightValue(nil)
}

更新

此功能现已添加到 IOS-Charts 的最新版本中 pull request

ChartViewDelegate 已更新为现在包含以下方法。

public protocol ChartViewDelegate
{
     ...

    /// Called when a user stop highlighting values while panning
    @objc optional func chartViewDidEndPanning(_ chartView: ChartViewBase)
} 

您所要做的就是在您实施 ChartViewDelegate 的任何地方实施该方法(图表 viewController)。

原答案子ClassLineChartView

@JGuo 的答案有效,但它需要您更改 pod 内的代码。这意味着每次更新 pod 时,您都必须重新实现这些更改。您可以在不修改 pod 的情况下获得相同的结果,如下所示:

创建协议

import Charts

@objc protocol MyChartViewDelegate {
    @objc optional func chartValueNoLongerSelected(_ chartView: MyLineChartView)
}

SubClass LineChartView

open class MyLineChartView: LineChartView {

    @objc weak var myChartViewDelegate: MyChartViewDelegate?

    private var touchesMoved = false

    // Haptic Feedback
    private let impactGenerator = UIImpactFeedbackGenerator(style: .light)
    private let selectionGenerator = UISelectionFeedbackGenerator()

    override open func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        // This is here to prevent the UITapGesture from blocking touches moved from firing
        if gestureRecognizer.isKind(of: NSUITapGestureRecognizer.classForCoder()){
            return false
        }
        return super.gestureRecognizerShouldBegin(gestureRecognizer)
    }

    override open func nsuiTouchesBegan(_ touches: Set<NSUITouch>, withEvent event: NSUIEvent?) {
        impactGenerator.impactOccurred()
        selectionGenerator.prepare()
        // adds the highlight to the graph when tapped
        super.nsuiTouchesBegan(touches, withEvent: event)
        touchesMoved = false
        if let touch = touches.first {
            let h = getHighlightByTouchPoint(touch.location(in: self))

            if h === nil || h == self.lastHighlighted {
                lastHighlighted = nil
                highlightValue(nil, callDelegate: true)
            }
            else {
                lastHighlighted = h
                highlightValue(h, callDelegate: true)
            }
        }
    }

    open override func nsuiTouchesEnded(_ touches: Set<NSUITouch>, withEvent event: NSUIEvent?) {
        super.nsuiTouchesEnded(touches, withEvent: event)
        myChartViewDelegate?.chartValueNoLongerSelected?(self) // remove the highlight
    }

    open override func nsuiTouchesCancelled(_ touches: Set<NSUITouch>?, withEvent event: NSUIEvent?) {
        super.nsuiTouchesCancelled(touches, withEvent: event)
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
            // if a tap turns into a panGesture touches cancelled is called this prevents the highlight from being moved
            if !self.touchesMoved {
                self.myChartViewDelegate?.chartValueNoLongerSelected?(self)
            }
        }
    }

    override open func nsuiTouchesMoved(_ touches: Set<NSUITouch>, withEvent event: NSUIEvent?) {
        super.nsuiTouchesMoved(touches, withEvent: event)
        touchesMoved = true

        if let touch = touches.first {
            let h = getHighlightByTouchPoint(touch.location(in: self))

            if h === nil {
                lastHighlighted = nil
                highlightValue(nil, callDelegate: true)
            }
            else if h == self.lastHighlighted {
                return
            }
            else {
                lastHighlighted = h
                highlightValue(h, callDelegate: true)
                selectionGenerator.selectionChanged()
            }
        }
    }
}

创建图表

let lineChartView = MyLineChartView()

override func viewDidLoad() {
    super.viewDidLoad()
    lineChartView.delegate = self // built in delegate for user interaction
    lineChartView.myChartViewDelegate = self // delegate with our additions (knowing when a value is no longer selected)
    lineChartView.highlightPerTapEnabled = false // disable tap gesture to highlight
    lineChartView.highlightPerDragEnabled = false // disable pan gesture
}

实施代表

extension MyViewController: ChartViewDelegate {

    func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) {
        // Do something on selection
    }

}

extension MyViewController: MyChartViewDelegate {

    func chartValueNoLongerSelected(_ chartView: FlyLineChartView) {
        // Do something on deselection
    }
}