Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import UIKit
- typealias ConnectionDrawing = Void -> Void
- class LineGraphView: UIView {
- var data = [Double]()
- var offset: CGFloat = 0.0
- var shownRange = 0 ..< 0
- var maximumValueShown: Double = 0.0
- var gestureRecognizer = UISwipeGestureRecognizer()
- var pointBorderColor = UIColor.whiteColor()
- var pointFillerColor = UIColor.lightGrayColor()
- var lineColor = UIColor.lightGrayColor()
- func configure(data data: [Double], shownRange: Range<Int>, maximumValueShown: Double, offset: CGFloat? = nil) {
- self.data = data
- self.maximumValueShown = maximumValueShown
- self.shownRange = shownRange // Must set range before using gap
- self.offset = offset ?? gap / 2
- gestureRecognizer.delegate = self
- addGestureRecognizer(gestureRecognizer)
- updateConnections()
- }
- override func drawRect(rect: CGRect) {
- for draw in lineDrawings {
- draw()
- }
- for draw in circleDrawings {
- draw()
- }
- }
- }
- extension LineGraphView: PointConvertable {
- var gap: CGFloat {
- return horizontalGap(amountOfPointsRendered: shownRange.count, viewWidth: bounds.size.width)
- }
- func pointsToShow() -> [CGPoint?] {
- return pointsFromData(data, withinRange: shownRange, maximumValueShown: maximumValueShown, inFrame: bounds, horizontalOffset: offset, includeLeadingAndTrailingPoints: true)
- }
- }
- // MARK: Drawing
- private var lineDrawings = [ConnectionDrawing]()
- private var circleDrawings = [ConnectionDrawing]()
- extension LineGraphView {
- func updateConnections() {
- removeConnections()
- let centers = pointsToShow()
- for (index, centerOne) in centers.enumerate() where index < centers.count - 1 {
- if let centerOne = centerOne, centerTwo = centers[index + 1] {
- addLineBetween(pointA: centerOne, pointB: centerTwo)
- }
- }
- for center in centers where center != nil {
- addCircle(center: center!)
- }
- }
- private func addLineBetween(pointA pointA: CGPoint, pointB: CGPoint) {
- lineDrawings.append({
- let path = UIBezierPath()
- path.lineWidth = 2.0
- path.moveToPoint(pointA)
- path.addLineToPoint(pointB)
- self.lineColor.setStroke()
- path.stroke()
- })
- setNeedsDisplay()
- }
- private func addCircle(center center: CGPoint) {
- circleDrawings.append({
- self.drawCircle(center: center, sizeSquare: 10, color: self.pointBorderColor)
- self.drawCircle(center: center, sizeSquare: 8, color: self.pointFillerColor)
- })
- setNeedsDisplay()
- }
- private func rectFromCenter(center: CGPoint, size: CGSize) -> CGRect {
- return CGRectMake(center.x - (size.width / 2), center.y - (size.height / 2), size.width, size.height)
- }
- private func drawCircle(center center: CGPoint, sizeSquare: CGFloat, color: UIColor) {
- let rect = rectFromCenter(center, size: CGSize(width: sizeSquare, height: sizeSquare))
- let path = UIBezierPath(ovalInRect: rect)
- color.setFill()
- path.fill()
- }
- private func removeConnections() {
- lineDrawings = []
- circleDrawings = []
- setNeedsDisplay()
- }
- }
- // MARK: Swipe to Shift Foward/Back
- private enum Shift {
- case Left, Right
- }
- private var originalStartIndex = 0
- private var touchPoint: CGPoint?
- extension LineGraphView: UIGestureRecognizerDelegate {
- override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
- originalStartIndex = shownRange.startIndex
- touchPoint = touches.first?.locationInView(self)
- }
- override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
- guard let oldPoint = touchPoint, newPoint = touches.first?.locationInView(self)
- else { return }
- let difference = newPoint.x - oldPoint.x
- if difference > 20 {
- shiftRange(.Left)
- } else if difference < -20 {
- shiftRange(.Right)
- }
- }
- private func shiftRange(shift: Shift) {
- switch shift {
- case .Left where shownRange.startIndex > 0:
- let startIndex = shownRange.startIndex - shownRange.count
- let endIndex = shownRange.endIndex - shownRange.count
- shownRange = shownRange.startIndex == originalStartIndex ? startIndex ..< endIndex : shownRange
- case .Right where shownRange.endIndex < data.count:
- let startIndex = shownRange.startIndex + shownRange.count
- let endIndex = shownRange.endIndex + shownRange.count
- shownRange = shownRange.startIndex == originalStartIndex ? startIndex ..< endIndex : shownRange
- default:
- break
- }
- updateConnections()
- }
- }
- protocol PointConvertable {
- func horizontalGap(amountOfPointsRendered rendered: Int, viewWidth: CGFloat) -> CGFloat
- func pointFromData(data: Double, atIndex index: Int, inFrame frame: CGRect, horizontalOffset offset: CGFloat, amountOfPointsRendered amount: Int, maximumValueShown maxValue: Double) -> CGPoint
- func pointsFromData(data: [Double], withinRange range: Range<Int>, maximumValueShown maxValue: Double, inFrame frame: CGRect, horizontalOffset offset: CGFloat, includeLeadingAndTrailingPoints hasLeadingTrailing: Bool) -> [CGPoint?]
- }
- extension PointConvertable {
- private func distanceFromTop(figureValue: Double, maxValue: Double, viewHeight: CGFloat) -> CGFloat {
- return viewHeight - (CGFloat(figureValue / maxValue) * viewHeight)
- }
- private func distanceFromLeft(figureValue: Double, indexOfFigureShown figureIndex: Int, horizontalGap gap: CGFloat, horizontalOffset offset: CGFloat) -> CGFloat {
- return offset + (gap * CGFloat(figureIndex))
- }
- func horizontalGap(amountOfPointsRendered rendered: Int, viewWidth: CGFloat) -> CGFloat {
- return viewWidth / CGFloat(rendered)
- }
- func pointFromData(data: Double, atIndex index: Int, inFrame frame: CGRect, horizontalOffset offset: CGFloat = 0, amountOfPointsRendered amount: Int, maximumValueShown maxValue: Double) -> CGPoint {
- let figure = data
- let viewHeight = frame.size.height
- let viewWidth = frame.size.width
- let gap = horizontalGap(amountOfPointsRendered: amount, viewWidth: viewWidth)
- let xPoint = distanceFromLeft(figure, indexOfFigureShown: index, horizontalGap: gap, horizontalOffset: offset)
- let yPoint = distanceFromTop(figure, maxValue: maxValue, viewHeight: viewHeight)
- return CGPoint(x: xPoint, y: yPoint)
- }
- func pointsFromData(data: [Double], withinRange range: Range<Int>, maximumValueShown maxValue: Double, inFrame frame: CGRect, horizontalOffset offset: CGFloat, includeLeadingAndTrailingPoints hasLeadingTrailing: Bool = false) -> [CGPoint?] {
- let startIndex = hasLeadingTrailing ? range.startIndex - 1 : range.startIndex
- let endIndex = hasLeadingTrailing ? range.endIndex + 1 : range.endIndex
- var points = [CGPoint?]()
- var shownIndex = hasLeadingTrailing ? -1 : 0
- for i in startIndex ..< endIndex {
- guard i >= 0 && i < data.count else {
- points.append(nil)
- shownIndex += 1
- continue
- }
- let figure = data[i]
- points.append(pointFromData(figure, atIndex: shownIndex, inFrame: frame, horizontalOffset: offset, amountOfPointsRendered: range.count, maximumValueShown: maxValue))
- shownIndex += 1
- }
- return points
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement