BalloonMarker.swift 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. //
  2. // BalloonMarker.swift
  3. // ChartsDemo
  4. //
  5. // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda
  6. // A port of MPAndroidChart for iOS
  7. // Licensed under Apache License 2.0
  8. //
  9. // https://github.com/danielgindi/Charts
  10. //
  11. import Foundation
  12. import Charts
  13. #if canImport(UIKit)
  14. import UIKit
  15. #endif
  16. open class BalloonMarker: MarkerImage
  17. {
  18. @objc open var color: UIColor
  19. @objc open var arrowSize = CGSize(width: 15, height: 11)
  20. @objc open var font: UIFont
  21. @objc open var textColor: UIColor
  22. @objc open var insets: UIEdgeInsets
  23. @objc open var minimumSize = CGSize()
  24. fileprivate var label: String?
  25. fileprivate var _labelSize: CGSize = CGSize()
  26. fileprivate var _paragraphStyle: NSMutableParagraphStyle?
  27. fileprivate var _drawAttributes = [NSAttributedString.Key : Any]()
  28. @objc public init(color: UIColor, font: UIFont, textColor: UIColor, insets: UIEdgeInsets)
  29. {
  30. self.color = color
  31. self.font = font
  32. self.textColor = textColor
  33. self.insets = insets
  34. _paragraphStyle = NSParagraphStyle.default.mutableCopy() as? NSMutableParagraphStyle
  35. _paragraphStyle?.alignment = .center
  36. super.init()
  37. }
  38. open override func offsetForDrawing(atPoint point: CGPoint) -> CGPoint
  39. {
  40. var offset = self.offset
  41. var size = self.size
  42. if size.width == 0.0 && image != nil
  43. {
  44. size.width = image!.size.width
  45. }
  46. if size.height == 0.0 && image != nil
  47. {
  48. size.height = image!.size.height
  49. }
  50. let width = size.width
  51. let height = size.height
  52. let padding: CGFloat = 8.0
  53. var origin = point
  54. origin.x -= width / 2
  55. origin.y -= height
  56. if origin.x + offset.x < 0.0
  57. {
  58. offset.x = -origin.x + padding
  59. }
  60. else if let chart = chartView,
  61. origin.x + width + offset.x > chart.bounds.size.width
  62. {
  63. offset.x = chart.bounds.size.width - origin.x - width - padding
  64. }
  65. if origin.y + offset.y < 0
  66. {
  67. offset.y = height + padding;
  68. }
  69. else if let chart = chartView,
  70. origin.y + height + offset.y > chart.bounds.size.height
  71. {
  72. offset.y = chart.bounds.size.height - origin.y - height - padding
  73. }
  74. return offset
  75. }
  76. open override func draw(context: CGContext, point: CGPoint)
  77. {
  78. guard let label = label else { return }
  79. let offset = self.offsetForDrawing(atPoint: point)
  80. let size = self.size
  81. var rect = CGRect(
  82. origin: CGPoint(
  83. x: point.x + offset.x,
  84. y: point.y + offset.y),
  85. size: size)
  86. rect.origin.x -= size.width / 2.0
  87. rect.origin.y -= size.height
  88. context.saveGState()
  89. context.setFillColor(color.cgColor)
  90. if offset.y > 0
  91. {
  92. context.beginPath()
  93. context.move(to: CGPoint(
  94. x: rect.origin.x,
  95. y: rect.origin.y + arrowSize.height))
  96. context.addLine(to: CGPoint(
  97. x: rect.origin.x + (rect.size.width - arrowSize.width) / 2.0,
  98. y: rect.origin.y + arrowSize.height))
  99. //arrow vertex
  100. context.addLine(to: CGPoint(
  101. x: point.x,
  102. y: point.y))
  103. context.addLine(to: CGPoint(
  104. x: rect.origin.x + (rect.size.width + arrowSize.width) / 2.0,
  105. y: rect.origin.y + arrowSize.height))
  106. context.addLine(to: CGPoint(
  107. x: rect.origin.x + rect.size.width,
  108. y: rect.origin.y + arrowSize.height))
  109. context.addLine(to: CGPoint(
  110. x: rect.origin.x + rect.size.width,
  111. y: rect.origin.y + rect.size.height))
  112. context.addLine(to: CGPoint(
  113. x: rect.origin.x,
  114. y: rect.origin.y + rect.size.height))
  115. context.addLine(to: CGPoint(
  116. x: rect.origin.x,
  117. y: rect.origin.y + arrowSize.height))
  118. context.fillPath()
  119. }
  120. else
  121. {
  122. context.beginPath()
  123. context.move(to: CGPoint(
  124. x: rect.origin.x,
  125. y: rect.origin.y))
  126. context.addLine(to: CGPoint(
  127. x: rect.origin.x + rect.size.width,
  128. y: rect.origin.y))
  129. context.addLine(to: CGPoint(
  130. x: rect.origin.x + rect.size.width,
  131. y: rect.origin.y + rect.size.height - arrowSize.height))
  132. context.addLine(to: CGPoint(
  133. x: rect.origin.x + (rect.size.width + arrowSize.width) / 2.0,
  134. y: rect.origin.y + rect.size.height - arrowSize.height))
  135. //arrow vertex
  136. context.addLine(to: CGPoint(
  137. x: point.x,
  138. y: point.y))
  139. context.addLine(to: CGPoint(
  140. x: rect.origin.x + (rect.size.width - arrowSize.width) / 2.0,
  141. y: rect.origin.y + rect.size.height - arrowSize.height))
  142. context.addLine(to: CGPoint(
  143. x: rect.origin.x,
  144. y: rect.origin.y + rect.size.height - arrowSize.height))
  145. context.addLine(to: CGPoint(
  146. x: rect.origin.x,
  147. y: rect.origin.y))
  148. context.fillPath()
  149. }
  150. if offset.y > 0 {
  151. rect.origin.y += self.insets.top + arrowSize.height
  152. } else {
  153. rect.origin.y += self.insets.top
  154. }
  155. rect.size.height -= self.insets.top + self.insets.bottom
  156. UIGraphicsPushContext(context)
  157. label.draw(in: rect, withAttributes: _drawAttributes)
  158. UIGraphicsPopContext()
  159. context.restoreGState()
  160. }
  161. open override func refreshContent(entry: ChartDataEntry, highlight: Highlight)
  162. {
  163. setLabel(String(entry.y))
  164. }
  165. @objc open func setLabel(_ newLabel: String)
  166. {
  167. label = newLabel
  168. _drawAttributes.removeAll()
  169. _drawAttributes[.font] = self.font
  170. _drawAttributes[.paragraphStyle] = _paragraphStyle
  171. _drawAttributes[.foregroundColor] = self.textColor
  172. _labelSize = label?.size(withAttributes: _drawAttributes) ?? CGSize.zero
  173. var size = CGSize()
  174. size.width = _labelSize.width + self.insets.left + self.insets.right
  175. size.height = _labelSize.height + self.insets.top + self.insets.bottom
  176. size.width = max(minimumSize.width, size.width)
  177. size.height = max(minimumSize.height, size.height)
  178. self.size = size
  179. }
  180. }