我创建了一个自定义 UIKit 视图,它是一个折线图视图,我希望可以选择在线条下方添加渐变颜色。到目前为止我已经做到了,所以我非常接近,但我不希望路径在图表底部闭合,我希望线条仅停在最后一个数据点。这是到目前为止的代码:
class LineChartView: UIView {
var dataPoints: [CGFloat] = [] // Data points for your chart
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .clear
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
super.draw(rect)
// Set up the bezier path
let path = UIBezierPath()
// Calculate the gap between each point
let horizontalGap = rect.width / CGFloat(dataPoints.count - 1)
// Start from the first data point
var startPoint = CGPoint(x: 0, y: dataPoints[0])
path.move(to: startPoint)
// Connect the points with lines
for i in 0..<dataPoints.count {
let nextPoint = CGPoint(x: CGFloat(i) * horizontalGap, y: dataPoints[i])
path.addLine(to: nextPoint)
startPoint = nextPoint
}
// Add extra points to close the path and fill the area under the line
path.addLine(to: CGPoint(x: rect.width, y: rect.height))
path.addLine(to: CGPoint(x: 0, y: rect.height))
let customBlueColor = UIColor.blue.withAlphaComponent(0.5)
// Get the cgColor of the custom blue color
let customBlueCGColor = customBlueColor.cgColor
// Create a gradient layer
let gradientLayer = CAGradientLayer()
gradientLayer.frame = CGRect(x: 0, y: 0, width: rect.width, height: rect.height)
gradientLayer.colors = [customBlueCGColor, UIColor.clear.cgColor]
gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.0) // Start from the top
gradientLayer.endPoint = CGPoint(x: 0.5, y: 1.0) // End at the bottom
// Create a shape layer for the gradient mask
let gradientMaskLayer = CAShapeLayer()
gradientMaskLayer.path = path.cgPath
// Add the gradient mask to the gradient layer
gradientLayer.mask = gradientMaskLayer
// Add the gradient layer to the view's layer
layer.addSublayer(gradientLayer)
// Create a shape layer for the line
let lineLayer = CAShapeLayer()
lineLayer.fillColor = UIColor.clear.cgColor
lineLayer.strokeColor = UIColor.blue.cgColor // Set color
lineLayer.lineWidth = 3.0
lineLayer.lineCap = .round
lineLayer.lineJoin = .round
lineLayer.path = path.cgPath
// Add the line layer to the view's layer
layer.addSublayer(lineLayer)
// Draw X-axis
let xAxisLayer = CAShapeLayer()
xAxisLayer.strokeColor = UIColor.black.cgColor
xAxisLayer.lineWidth = 3.0
let xAxisPath = UIBezierPath()
xAxisPath.move(to: CGPoint(x: 0, y: bounds.height))
xAxisPath.addLine(to: CGPoint(x: bounds.width, y: bounds.height))
xAxisLayer.path = xAxisPath.cgPath
layer.addSublayer(xAxisLayer)
// Draw Y-axis
let yAxisLayer = CAShapeLayer()
yAxisLayer.strokeColor = UIColor.black.cgColor
yAxisLayer.lineWidth = 3.0
let yAxisPath = UIBezierPath()
yAxisPath.move(to: CGPoint(x: 0, y: 0))
yAxisPath.addLine(to: CGPoint(x: 0, y: bounds.height))
yAxisLayer.path = yAxisPath.cgPath
layer.addSublayer(yAxisLayer)
}
}
然后,如果您想将其放入视图控制器中,请执行以下操作:
class ViewController: UIViewController {
let randomNumbers: [CGFloat] = Array(repeating: 0.0, count: 20).map { _ in CGFloat.random(in: 1..<200) }
private lazy var lineChartView: LineChartView = {
let view = LineChartView()
view.dataPoints = randomNumbers
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(lineChartView)
configure()
}
private func configure() {
NSLayoutConstraint.activate([
lineChartView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
lineChartView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
lineChartView.heightAnchor.constraint(equalToConstant: 300),
lineChartView.widthAnchor.constraint(equalToConstant: 400)
])
}
}
首先,不要在
override func draw(_ rect: CGRect)
中执行此操作...这将被多次调用,并且您将添加多个形状图层。
在初始化视图时添加图层 - 并设置其不变的属性。然后在
layoutSubviews()
中定义并设置路径和掩码。
其次,摆脱不需要的蓝线段的最简单方法是创建两条路径:
// for blue line
let linePath = UIBezierPath()
// for gradient filled area
let gradPath = UIBezierPath()
使用相同的 moveTo / lineTo 代码构建两条路径,但仅添加最后一个点并关闭渐变路径。
这是经过上述更改的班级:
class LineChartView: UIView {
var dataPoints: [CGFloat] = [] // Data points for your chart
// Create a gradient layer
let gradientLayer = CAGradientLayer()
// Create a shape layer for the line
let lineLayer = CAShapeLayer()
let xAxisLayer = CAShapeLayer()
let yAxisLayer = CAShapeLayer()
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .clear
let customBlueColor = UIColor.blue.withAlphaComponent(0.5)
// Get the cgColor of the custom blue color
let customBlueCGColor = customBlueColor.cgColor
layer.addSublayer(gradientLayer)
layer.addSublayer(lineLayer)
layer.addSublayer(xAxisLayer)
layer.addSublayer(yAxisLayer)
// gradient layer properties
gradientLayer.colors = [customBlueCGColor, UIColor.clear.cgColor]
gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.0) // Start from the top
gradientLayer.endPoint = CGPoint(x: 0.5, y: 1.0) // End at the bottom
// line layer properties
lineLayer.fillColor = UIColor.clear.cgColor
lineLayer.strokeColor = UIColor.blue.cgColor // Set color
lineLayer.lineWidth = 3.0
lineLayer.lineCap = .round
lineLayer.lineJoin = .round
yAxisLayer.strokeColor = UIColor.black.cgColor
yAxisLayer.lineWidth = 3.0
}
override func layoutSubviews() {
super.layoutSubviews()
let rect = bounds
// for blue line
let linePath = UIBezierPath()
// for gradient filled area
let gradPath = UIBezierPath()
// Calculate the gap between each point
let horizontalGap = rect.width / CGFloat(dataPoints.count - 1)
// Start from the first data point
var startPoint = CGPoint(x: 0, y: dataPoints[0])
linePath.move(to: startPoint)
gradPath.move(to: startPoint)
// Connect the points with lines
for i in 0..<dataPoints.count {
let nextPoint = CGPoint(x: CGFloat(i) * horizontalGap, y: dataPoints[i])
linePath.addLine(to: nextPoint)
gradPath.addLine(to: nextPoint)
startPoint = nextPoint
}
// Add extra points to close the path and fill the area under the line
// but don't close the blue-line path
gradPath.addLine(to: CGPoint(x: rect.width, y: rect.height))
gradPath.addLine(to: CGPoint(x: 0, y: rect.height))
// Create a shape layer for the gradient mask
let gradientMaskLayer = CAShapeLayer()
gradientMaskLayer.path = gradPath.cgPath
gradientLayer.frame = CGRect(x: 0, y: 0, width: rect.width, height: rect.height)
// Add the gradient mask to the gradient layer
gradientLayer.mask = gradientMaskLayer
lineLayer.path = linePath.cgPath
// Draw X-axis
xAxisLayer.strokeColor = UIColor.black.cgColor
xAxisLayer.lineWidth = 3.0
let xAxisPath = UIBezierPath()
xAxisPath.move(to: CGPoint(x: 0, y: bounds.height))
xAxisPath.addLine(to: CGPoint(x: bounds.width, y: bounds.height))
xAxisLayer.path = xAxisPath.cgPath
// Draw Y-axis
let yAxisPath = UIBezierPath()
yAxisPath.move(to: CGPoint(x: 0, y: 0))
yAxisPath.addLine(to: CGPoint(x: 0, y: bounds.height))
yAxisLayer.path = yAxisPath.cgPath
}
}
现在看起来像这样: