-
Notifications
You must be signed in to change notification settings - Fork 462
Expand file tree
/
Copy pathLineDrawingLayer.swift
More file actions
144 lines (107 loc) · 6.04 KB
/
LineDrawingLayer.swift
File metadata and controls
144 lines (107 loc) · 6.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import UIKit
internal class LineDrawingLayer : ScrollableGraphViewDrawingLayer {
private var currentLinePath = UIBezierPath()
private var lineStyle: ScrollableGraphViewLineStyle
private var shouldFill: Bool
private var lineCurviness: CGFloat
init(frame: CGRect, lineWidth: CGFloat, lineColor: UIColor, lineStyle: ScrollableGraphViewLineStyle, lineJoin: String, lineCap: String, shouldFill: Bool, lineCurviness: CGFloat) {
self.lineStyle = lineStyle
self.shouldFill = shouldFill
self.lineCurviness = lineCurviness
super.init(viewportWidth: frame.size.width, viewportHeight: frame.size.height)
self.lineWidth = lineWidth
self.strokeColor = lineColor.cgColor
self.lineJoin = convertToCAShapeLayerLineJoin(lineJoin)
self.lineCap = convertToCAShapeLayerLineCap(lineCap)
// Setup
self.fillColor = UIColor.clear.cgColor // This is handled by the fill drawing layer.
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(layer: Any) {
// Default values
lineStyle = .smooth
shouldFill = false
lineCurviness = 1.0
super.init(layer: layer)
}
internal func createLinePath() -> UIBezierPath {
guard let owner = owner else {
return UIBezierPath()
}
// Can't really do anything without the delegate.
guard let delegate = self.owner?.graphViewDrawingDelegate else {
return currentLinePath
}
currentLinePath.removeAllPoints()
let pathSegmentAdder = lineStyle == .straight ? addStraightLineSegment : addCurvedLineSegment
let activePointsInterval = delegate.intervalForActivePoints()
let pointPadding = delegate.paddingForPoints()
let min = delegate.rangeForActivePoints().min
zeroYPosition = delegate.calculatePosition(atIndex: 0, value: min).y
let viewport = delegate.currentViewport()
let viewportWidth = viewport.width
let viewportHeight = viewport.height
// Connect the line to the starting edge if we are filling it.
if(shouldFill) {
// Add a line from the base of the graph to the first data point.
let firstDataPoint = owner.graphPoint(forIndex: activePointsInterval.lowerBound)
let viewportLeftZero = CGPoint(x: firstDataPoint.location.x - (pointPadding.leftmostPointPadding), y: zeroYPosition)
let leftFarEdgeTop = CGPoint(x: firstDataPoint.location.x - (pointPadding.leftmostPointPadding + viewportWidth), y: zeroYPosition)
let leftFarEdgeBottom = CGPoint(x: firstDataPoint.location.x - (pointPadding.leftmostPointPadding + viewportWidth), y: viewportHeight)
currentLinePath.move(to: leftFarEdgeBottom)
pathSegmentAdder(leftFarEdgeBottom, leftFarEdgeTop, currentLinePath)
pathSegmentAdder(leftFarEdgeTop, viewportLeftZero, currentLinePath)
pathSegmentAdder(viewportLeftZero, CGPoint(x: firstDataPoint.location.x, y: firstDataPoint.location.y), currentLinePath)
}
else {
let firstDataPoint = owner.graphPoint(forIndex: activePointsInterval.lowerBound)
currentLinePath.move(to: firstDataPoint.location)
}
// Connect each point on the graph with a segment.
for i in activePointsInterval.lowerBound ..< activePointsInterval.upperBound - 1 {
let startPoint = owner.graphPoint(forIndex: i).location
let endPoint = owner.graphPoint(forIndex: i+1).location
pathSegmentAdder(startPoint, endPoint, currentLinePath)
}
// Connect the line to the ending edge if we are filling it.
if(shouldFill) {
// Add a line from the last data point to the base of the graph.
let lastDataPoint = owner.graphPoint(forIndex: activePointsInterval.upperBound - 1).location
let viewportRightZero = CGPoint(x: lastDataPoint.x + (pointPadding.rightmostPointPadding), y: zeroYPosition)
let rightFarEdgeTop = CGPoint(x: lastDataPoint.x + (pointPadding.rightmostPointPadding + viewportWidth), y: zeroYPosition)
let rightFarEdgeBottom = CGPoint(x: lastDataPoint.x + (pointPadding.rightmostPointPadding + viewportWidth), y: viewportHeight)
pathSegmentAdder(lastDataPoint, viewportRightZero, currentLinePath)
pathSegmentAdder(viewportRightZero, rightFarEdgeTop, currentLinePath)
pathSegmentAdder(rightFarEdgeTop, rightFarEdgeBottom, currentLinePath)
}
return currentLinePath
}
private func addStraightLineSegment(startPoint: CGPoint, endPoint: CGPoint, inPath path: UIBezierPath) {
path.addLine(to: endPoint)
}
private func addCurvedLineSegment(startPoint: CGPoint, endPoint: CGPoint, inPath path: UIBezierPath) {
// calculate control points
let difference = endPoint.x - startPoint.x
var x = startPoint.x + (difference * lineCurviness)
var y = startPoint.y
let controlPointOne = CGPoint(x: x, y: y)
x = endPoint.x - (difference * lineCurviness)
y = endPoint.y
let controlPointTwo = CGPoint(x: x, y: y)
// add curve from start to end
currentLinePath.addCurve(to: endPoint, controlPoint1: controlPointOne, controlPoint2: controlPointTwo)
}
override func updatePath() {
self.path = createLinePath().cgPath
}
}
// Helper function inserted by Swift 4.2 migrator.
fileprivate func convertToCAShapeLayerLineJoin(_ input: String) -> CAShapeLayerLineJoin {
return CAShapeLayerLineJoin(rawValue: input)
}
// Helper function inserted by Swift 4.2 migrator.
fileprivate func convertToCAShapeLayerLineCap(_ input: String) -> CAShapeLayerLineCap {
return CAShapeLayerLineCap(rawValue: input)
}