UIKit 自定义折线图视图,线条下方带有渐变遮罩

问题描述 投票:0回答:1

我创建了一个自定义 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)
        ])
    }
}

enter image description here

ios swift xcode charts uikit
1个回答
0
投票

首先,不要在

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
    }
}

现在看起来像这样:

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.