使用 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
}
}
使用 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
}
}