Advertisement
Guest User

Untitled

a guest
Jul 26th, 2016
60
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.51 KB | None | 0 0
  1. import UIKit
  2.  
  3. typealias ConnectionDrawing = Void -> Void
  4.  
  5. class LineGraphView: UIView {
  6.  
  7. var data = [Double]()
  8. var offset: CGFloat = 0.0
  9. var shownRange = 0 ..< 0
  10. var maximumValueShown: Double = 0.0
  11. var gestureRecognizer = UISwipeGestureRecognizer()
  12. var pointBorderColor = UIColor.whiteColor()
  13. var pointFillerColor = UIColor.lightGrayColor()
  14. var lineColor = UIColor.lightGrayColor()
  15.  
  16. func configure(data data: [Double], shownRange: Range<Int>, maximumValueShown: Double, offset: CGFloat? = nil) {
  17. self.data = data
  18. self.maximumValueShown = maximumValueShown
  19. self.shownRange = shownRange // Must set range before using gap
  20. self.offset = offset ?? gap / 2
  21. gestureRecognizer.delegate = self
  22. addGestureRecognizer(gestureRecognizer)
  23.  
  24. updateConnections()
  25. }
  26.  
  27. override func drawRect(rect: CGRect) {
  28. for draw in lineDrawings {
  29. draw()
  30. }
  31. for draw in circleDrawings {
  32. draw()
  33. }
  34. }
  35.  
  36. }
  37.  
  38. extension LineGraphView: PointConvertable {
  39.  
  40. var gap: CGFloat {
  41. return horizontalGap(amountOfPointsRendered: shownRange.count, viewWidth: bounds.size.width)
  42. }
  43.  
  44. func pointsToShow() -> [CGPoint?] {
  45. return pointsFromData(data, withinRange: shownRange, maximumValueShown: maximumValueShown, inFrame: bounds, horizontalOffset: offset, includeLeadingAndTrailingPoints: true)
  46. }
  47.  
  48. }
  49.  
  50. // MARK: Drawing
  51. private var lineDrawings = [ConnectionDrawing]()
  52. private var circleDrawings = [ConnectionDrawing]()
  53.  
  54. extension LineGraphView {
  55.  
  56. func updateConnections() {
  57. removeConnections()
  58. let centers = pointsToShow()
  59.  
  60. for (index, centerOne) in centers.enumerate() where index < centers.count - 1 {
  61. if let centerOne = centerOne, centerTwo = centers[index + 1] {
  62. addLineBetween(pointA: centerOne, pointB: centerTwo)
  63. }
  64. }
  65. for center in centers where center != nil {
  66. addCircle(center: center!)
  67. }
  68. }
  69.  
  70. private func addLineBetween(pointA pointA: CGPoint, pointB: CGPoint) {
  71. lineDrawings.append({
  72.  
  73. let path = UIBezierPath()
  74. path.lineWidth = 2.0
  75. path.moveToPoint(pointA)
  76. path.addLineToPoint(pointB)
  77. self.lineColor.setStroke()
  78.  
  79. path.stroke()
  80. })
  81. setNeedsDisplay()
  82. }
  83.  
  84. private func addCircle(center center: CGPoint) {
  85. circleDrawings.append({
  86.  
  87. self.drawCircle(center: center, sizeSquare: 10, color: self.pointBorderColor)
  88. self.drawCircle(center: center, sizeSquare: 8, color: self.pointFillerColor)
  89. })
  90. setNeedsDisplay()
  91. }
  92.  
  93. private func rectFromCenter(center: CGPoint, size: CGSize) -> CGRect {
  94. return CGRectMake(center.x - (size.width / 2), center.y - (size.height / 2), size.width, size.height)
  95. }
  96.  
  97. private func drawCircle(center center: CGPoint, sizeSquare: CGFloat, color: UIColor) {
  98. let rect = rectFromCenter(center, size: CGSize(width: sizeSquare, height: sizeSquare))
  99. let path = UIBezierPath(ovalInRect: rect)
  100. color.setFill()
  101. path.fill()
  102. }
  103.  
  104. private func removeConnections() {
  105. lineDrawings = []
  106. circleDrawings = []
  107. setNeedsDisplay()
  108. }
  109.  
  110. }
  111.  
  112. // MARK: Swipe to Shift Foward/Back
  113. private enum Shift {
  114. case Left, Right
  115. }
  116.  
  117. private var originalStartIndex = 0
  118. private var touchPoint: CGPoint?
  119.  
  120. extension LineGraphView: UIGestureRecognizerDelegate {
  121.  
  122. override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
  123. originalStartIndex = shownRange.startIndex
  124. touchPoint = touches.first?.locationInView(self)
  125. }
  126.  
  127. override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
  128. guard let oldPoint = touchPoint, newPoint = touches.first?.locationInView(self)
  129. else { return }
  130.  
  131. let difference = newPoint.x - oldPoint.x
  132. if difference > 20 {
  133. shiftRange(.Left)
  134. } else if difference < -20 {
  135. shiftRange(.Right)
  136. }
  137.  
  138. }
  139.  
  140. private func shiftRange(shift: Shift) {
  141.  
  142. switch shift {
  143. case .Left where shownRange.startIndex > 0:
  144. let startIndex = shownRange.startIndex - shownRange.count
  145. let endIndex = shownRange.endIndex - shownRange.count
  146. shownRange = shownRange.startIndex == originalStartIndex ? startIndex ..< endIndex : shownRange
  147. case .Right where shownRange.endIndex < data.count:
  148. let startIndex = shownRange.startIndex + shownRange.count
  149. let endIndex = shownRange.endIndex + shownRange.count
  150. shownRange = shownRange.startIndex == originalStartIndex ? startIndex ..< endIndex : shownRange
  151. default:
  152. break
  153. }
  154.  
  155. updateConnections()
  156. }
  157.  
  158. }
  159.  
  160. protocol PointConvertable {
  161. func horizontalGap(amountOfPointsRendered rendered: Int, viewWidth: CGFloat) -> CGFloat
  162. func pointFromData(data: Double, atIndex index: Int, inFrame frame: CGRect, horizontalOffset offset: CGFloat, amountOfPointsRendered amount: Int, maximumValueShown maxValue: Double) -> CGPoint
  163. func pointsFromData(data: [Double], withinRange range: Range<Int>, maximumValueShown maxValue: Double, inFrame frame: CGRect, horizontalOffset offset: CGFloat, includeLeadingAndTrailingPoints hasLeadingTrailing: Bool) -> [CGPoint?]
  164. }
  165.  
  166. extension PointConvertable {
  167.  
  168. private func distanceFromTop(figureValue: Double, maxValue: Double, viewHeight: CGFloat) -> CGFloat {
  169.  
  170. return viewHeight - (CGFloat(figureValue / maxValue) * viewHeight)
  171. }
  172.  
  173. private func distanceFromLeft(figureValue: Double, indexOfFigureShown figureIndex: Int, horizontalGap gap: CGFloat, horizontalOffset offset: CGFloat) -> CGFloat {
  174.  
  175. return offset + (gap * CGFloat(figureIndex))
  176. }
  177.  
  178. func horizontalGap(amountOfPointsRendered rendered: Int, viewWidth: CGFloat) -> CGFloat {
  179.  
  180. return viewWidth / CGFloat(rendered)
  181. }
  182.  
  183. func pointFromData(data: Double, atIndex index: Int, inFrame frame: CGRect, horizontalOffset offset: CGFloat = 0, amountOfPointsRendered amount: Int, maximumValueShown maxValue: Double) -> CGPoint {
  184.  
  185. let figure = data
  186. let viewHeight = frame.size.height
  187. let viewWidth = frame.size.width
  188. let gap = horizontalGap(amountOfPointsRendered: amount, viewWidth: viewWidth)
  189. let xPoint = distanceFromLeft(figure, indexOfFigureShown: index, horizontalGap: gap, horizontalOffset: offset)
  190. let yPoint = distanceFromTop(figure, maxValue: maxValue, viewHeight: viewHeight)
  191.  
  192. return CGPoint(x: xPoint, y: yPoint)
  193. }
  194.  
  195. func pointsFromData(data: [Double], withinRange range: Range<Int>, maximumValueShown maxValue: Double, inFrame frame: CGRect, horizontalOffset offset: CGFloat, includeLeadingAndTrailingPoints hasLeadingTrailing: Bool = false) -> [CGPoint?] {
  196.  
  197. let startIndex = hasLeadingTrailing ? range.startIndex - 1 : range.startIndex
  198. let endIndex = hasLeadingTrailing ? range.endIndex + 1 : range.endIndex
  199.  
  200. var points = [CGPoint?]()
  201.  
  202. var shownIndex = hasLeadingTrailing ? -1 : 0
  203. for i in startIndex ..< endIndex {
  204. guard i >= 0 && i < data.count else {
  205. points.append(nil)
  206. shownIndex += 1
  207. continue
  208. }
  209.  
  210. let figure = data[i]
  211. points.append(pointFromData(figure, atIndex: shownIndex, inFrame: frame, horizontalOffset: offset, amountOfPointsRendered: range.count, maximumValueShown: maxValue))
  212. shownIndex += 1
  213. }
  214.  
  215. return points
  216. }
  217.  
  218. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement