diff --git a/ChartsDemo/Objective-C/Demos/RadarChartViewController.m b/ChartsDemo/Objective-C/Demos/RadarChartViewController.m index 1810924a86..7bc11569d1 100755 --- a/ChartsDemo/Objective-C/Demos/RadarChartViewController.m +++ b/ChartsDemo/Objective-C/Demos/RadarChartViewController.m @@ -59,7 +59,7 @@ - (void)viewDidLoad _chartView.innerWebColor = UIColor.lightGrayColor; _chartView.webAlpha = 1.0; - RadarMarkerView *marker = (RadarMarkerView *)[RadarMarkerView viewFromXib]; + RadarMarkerView *marker = (RadarMarkerView *)[RadarMarkerView viewFromXibIn:[NSBundle mainBundle]]; marker.chartView = _chartView; _chartView.marker = marker; diff --git a/ChartsDemo/Swift/Components/BalloonMarker.swift b/ChartsDemo/Swift/Components/BalloonMarker.swift index 2fe3e93141..bc0128ef8c 100755 --- a/ChartsDemo/Swift/Components/BalloonMarker.swift +++ b/ChartsDemo/Swift/Components/BalloonMarker.swift @@ -34,11 +34,47 @@ public class BalloonMarker: MarkerImage { } public 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) + var offset = self.offset + var size = self.size + + if size.width == 0.0 && image != nil + { + size.width = image!.size.width + } + if size.height == 0.0 && image != nil + { + size.height = image!.size.height + } + + let width = size.width + let height = size.height + let padding: CGFloat = 8.0 + + var origin = point + origin.x -= width / 2 + origin.y -= height + + if origin.x + offset.x < 0.0 + { + offset.x = -origin.x + padding + } + else if let chart = chartView, + origin.x + width + offset.x > chart.bounds.size.width + { + offset.x = chart.bounds.size.width - origin.x - width - padding + } + + if origin.y + offset.y < 0 + { + offset.y = height + padding; + } + else if let chart = chartView, + origin.y + height + offset.y > chart.bounds.size.height + { + offset.y = chart.bounds.size.height - origin.y - height - padding + } + + return offset } public override func draw(context: CGContext, point: CGPoint) { @@ -58,36 +94,71 @@ public class BalloonMarker: MarkerImage { context.saveGState() 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.x += self.insets.left - rect.origin.y += self.insets.top - rect.size.width -= self.insets.left + self.insets.right + + if offset.y > 0 { + context.beginPath() + context.move(to: CGPoint( + x: rect.origin.x, + y: rect.origin.y + arrowSize.height)) + context.addLine(to: CGPoint( + x: rect.origin.x + (rect.size.width - arrowSize.width) / 2.0, + y: rect.origin.y + arrowSize.height)) + //arrow vertex + context.addLine(to: CGPoint( + x: point.x, + y: point.y)) + context.addLine(to: CGPoint( + x: rect.origin.x + (rect.size.width + arrowSize.width) / 2.0, + y: rect.origin.y + arrowSize.height)) + context.addLine(to: CGPoint( + x: rect.origin.x + rect.size.width, + y: rect.origin.y + arrowSize.height)) + context.addLine(to: CGPoint( + x: rect.origin.x + rect.size.width, + y: rect.origin.y + rect.size.height)) + context.addLine(to: CGPoint( + x: rect.origin.x, + y: rect.origin.y + rect.size.height)) + context.addLine(to: CGPoint( + x: rect.origin.x, + y: rect.origin.y + arrowSize.height)) + context.fillPath() + } else { + 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)) + //arrow vertex + context.addLine(to: CGPoint( + x: point.x, + y: point.y)) + 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() + } + + if offset.y > 0 { + rect.origin.y += self.insets.top + arrowSize.height + } else { + rect.origin.y += self.insets.top + } + rect.size.height -= self.insets.top + self.insets.bottom UIGraphicsPushContext(context) diff --git a/Source/Charts/Charts/ChartViewBase.swift b/Source/Charts/Charts/ChartViewBase.swift index 712e3e6910..e82918f4de 100644 --- a/Source/Charts/Charts/ChartViewBase.swift +++ b/Source/Charts/Charts/ChartViewBase.swift @@ -424,9 +424,10 @@ open class ChartViewBase: NSUIView, ChartDataProvider, AnimatorDelegate /// This method will call the delegate. /// - parameter x: The x-value to highlight /// - parameter dataSetIndex: The dataset index to search in - @objc open func highlightValue(x: Double, dataSetIndex: Int) + /// - parameter dataIndex: The data index to search in (only used in CombinedChartView currently) + @objc open func highlightValue(x: Double, dataSetIndex: Int, dataIndex: Int = -1) { - highlightValue(x: x, dataSetIndex: dataSetIndex, callDelegate: true) + highlightValue(x: x, dataSetIndex: dataSetIndex, dataIndex: dataIndex, callDelegate: true) } /// Highlights the value at the given x-value and y-value in the given DataSet. @@ -435,19 +436,21 @@ open class ChartViewBase: NSUIView, ChartDataProvider, AnimatorDelegate /// - parameter x: The x-value to highlight /// - parameter y: The y-value to highlight. Supply `NaN` for "any" /// - parameter dataSetIndex: The dataset index to search in - @objc open func highlightValue(x: Double, y: Double, dataSetIndex: Int) + /// - parameter dataIndex: The data index to search in (only used in CombinedChartView currently) + @objc open func highlightValue(x: Double, y: Double, dataSetIndex: Int, dataIndex: Int = -1) { - highlightValue(x: x, y: y, dataSetIndex: dataSetIndex, callDelegate: true) + highlightValue(x: x, y: y, dataSetIndex: dataSetIndex, dataIndex: dataIndex, callDelegate: true) } /// Highlights any y-value at the given x-value in the given DataSet. /// Provide -1 as the dataSetIndex to undo all highlighting. /// - parameter x: The x-value to highlight /// - parameter dataSetIndex: The dataset index to search in + /// - parameter dataIndex: The data index to search in (only used in CombinedChartView currently) /// - parameter callDelegate: Should the delegate be called for this change - @objc open func highlightValue(x: Double, dataSetIndex: Int, callDelegate: Bool) + @objc open func highlightValue(x: Double, dataSetIndex: Int, dataIndex: Int = -1, callDelegate: Bool) { - highlightValue(x: x, y: Double.nan, dataSetIndex: dataSetIndex, callDelegate: callDelegate) + highlightValue(x: x, y: .nan, dataSetIndex: dataSetIndex, dataIndex: dataIndex, callDelegate: callDelegate) } /// Highlights the value at the given x-value and y-value in the given DataSet. @@ -455,8 +458,9 @@ open class ChartViewBase: NSUIView, ChartDataProvider, AnimatorDelegate /// - parameter x: The x-value to highlight /// - parameter y: The y-value to highlight. Supply `NaN` for "any" /// - parameter dataSetIndex: The dataset index to search in + /// - parameter dataIndex: The data index to search in (only used in CombinedChartView currently) /// - parameter callDelegate: Should the delegate be called for this change - @objc open func highlightValue(x: Double, y: Double, dataSetIndex: Int, callDelegate: Bool) + @objc open func highlightValue(x: Double, y: Double, dataSetIndex: Int, dataIndex: Int = -1, callDelegate: Bool) { guard let data = _data else { @@ -470,7 +474,7 @@ open class ChartViewBase: NSUIView, ChartDataProvider, AnimatorDelegate } else { - highlightValue(Highlight(x: x, y: y, dataSetIndex: dataSetIndex), callDelegate: callDelegate) + highlightValue(Highlight(x: x, y: y, dataSetIndex: dataSetIndex, dataIndex: dataIndex), callDelegate: callDelegate) } } diff --git a/Source/Charts/Components/MarkerView.swift b/Source/Charts/Components/MarkerView.swift index a051ff9968..094f4e2e9d 100644 --- a/Source/Charts/Components/MarkerView.swift +++ b/Source/Charts/Components/MarkerView.swift @@ -72,10 +72,10 @@ open class MarkerView: NSUIView, IMarker } @objc - open class func viewFromXib() -> MarkerView? + open class func viewFromXib(in bundle: Bundle = .main) -> MarkerView? { #if !os(OSX) - return Bundle.main.loadNibNamed( + return bundle.loadNibNamed( String(describing: self), owner: nil, options: nil)?[0] as? MarkerView @@ -84,7 +84,7 @@ open class MarkerView: NSUIView, IMarker var loadedObjects = NSArray() let loadedObjectsPointer = AutoreleasingUnsafeMutablePointer(&loadedObjects) - if Bundle.main.loadNibNamed( + if bundle.loadNibNamed( NSNib.Name(String(describing: self)), owner: nil, topLevelObjects: loadedObjectsPointer) diff --git a/Source/Charts/Data/Implementations/Standard/ChartDataSet.swift b/Source/Charts/Data/Implementations/Standard/ChartDataSet.swift index 716008fdd7..647c4bddf4 100644 --- a/Source/Charts/Data/Implementations/Standard/ChartDataSet.swift +++ b/Source/Charts/Data/Implementations/Standard/ChartDataSet.swift @@ -58,11 +58,19 @@ open class ChartDataSet: ChartBaseDataSet /// - note: Calls `notifyDataSetChanged()` after setting a new value. /// - returns: The array of y-values that this DataSet represents. /// the entries that this dataset represents / holds together - @objc open var values: [ChartDataEntry] { - didSet { + @objc open var values: [ChartDataEntry] + { + didSet + { + if isIndirectValuesCall { + isIndirectValuesCall = false + return + } notifyDataSetChanged() } } + // TODO: Temporary fix for performance. Will be removed in 4.0 + private var isIndirectValuesCall = false /// maximum y-value in the value array internal var _yMax: Double = -Double.greatestFiniteMagnitude @@ -386,7 +394,8 @@ open class ChartDataSet: ChartBaseDataSet open override func addEntry(_ e: ChartDataEntry) -> Bool { calcMinMax(entry: e) - + + isIndirectValuesCall = true values.append(e) return true @@ -401,6 +410,7 @@ open class ChartDataSet: ChartBaseDataSet { calcMinMax(entry: e) + isIndirectValuesCall = true if values.count > 0 && values.last!.x > e.x { var closestIndex = entryIndex(x: e.x, closestToY: e.y, rounding: .up) @@ -425,7 +435,8 @@ open class ChartDataSet: ChartBaseDataSet open override func removeEntry(_ entry: ChartDataEntry) -> Bool { var removed = false - + isIndirectValuesCall = true + for i in 0 ..< values.count { if values[i] === entry @@ -436,6 +447,8 @@ open class ChartDataSet: ChartBaseDataSet } } + notifyDataSetChanged() + return removed } diff --git a/Source/Charts/Highlight/Highlight.swift b/Source/Charts/Highlight/Highlight.swift index 2205db1131..e036b08464 100644 --- a/Source/Charts/Highlight/Highlight.swift +++ b/Source/Charts/Highlight/Highlight.swift @@ -128,11 +128,13 @@ open class Highlight: NSObject /// - parameter x: the x-value of the highlighted value /// - parameter y: the y-value of the highlighted value /// - parameter dataSetIndex: the index of the DataSet the highlighted value belongs to - @objc public init(x: Double, y: Double, dataSetIndex: Int) + /// - parameter dataIndex: The data index to search in (only used in CombinedChartView currently) + @objc public init(x: Double, y: Double, dataSetIndex: Int, dataIndex: Int = -1) { _x = x _y = y _dataSetIndex = dataSetIndex + self.dataIndex = dataIndex } /// - parameter x: the x-value of the highlighted value diff --git a/Source/Charts/Mark/BalloonMarker.swift b/Source/Charts/Mark/BalloonMarker.swift index 880c3831d0..e3483e2b8a 100644 --- a/Source/Charts/Mark/BalloonMarker.swift +++ b/Source/Charts/Mark/BalloonMarker.swift @@ -42,11 +42,47 @@ open class BalloonMarker: MarkerImage 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) + var offset = self.offset + var size = self.size + + if size.width == 0.0 && image != nil + { + size.width = image!.size.width + } + if size.height == 0.0 && image != nil + { + size.height = image!.size.height + } + + let width = size.width + let height = size.height + let padding: CGFloat = 8.0 + + var origin = point + origin.x -= width / 2 + origin.y -= height + + if origin.x + offset.x < 0.0 + { + offset.x = -origin.x + padding + } + else if let chart = chartView, + origin.x + width + offset.x > chart.bounds.size.width + { + offset.x = chart.bounds.size.width - origin.x - width - padding + } + + if origin.y + offset.y < 0 + { + offset.y = height + padding; + } + else if let chart = chartView, + origin.y + height + offset.y > chart.bounds.size.height + { + offset.y = chart.bounds.size.height - origin.y - height - padding + } + + return offset } func drawLabel(context: CGContext, point: CGPoint, label: String) { diff --git a/Source/Charts/Renderers/BubbleChartRenderer.swift b/Source/Charts/Renderers/BubbleChartRenderer.swift index 8498070c17..51e937e0fc 100644 --- a/Source/Charts/Renderers/BubbleChartRenderer.swift +++ b/Source/Charts/Renderers/BubbleChartRenderer.swift @@ -105,7 +105,7 @@ open class BubbleChartRenderer: BarLineScatterCandleBubbleRenderer guard viewPortHandler.isInBoundsRight(_pointBuffer.x - shapeHalf) else { break } - let color = dataSet.color(atIndex: Int(entry.x)) + let color = dataSet.color(atIndex: j) let rect = CGRect( x: _pointBuffer.x - shapeHalf, diff --git a/Source/Supporting Files/Info.plist b/Source/Supporting Files/Info.plist index 4c2f2e9bac..479df9665c 100644 --- a/Source/Supporting Files/Info.plist +++ b/Source/Supporting Files/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 3.0.3 + 3.0.5 CFBundleSignature ???? CFBundleVersion