bezierPath setfill with diff colors 不起作用(创建爬山颜色编码)

bezierPath setfill with diff colours Not Working ( to create a mountain climb color coding )

我的目标是创建一个类似于下图的图表。其中差异颜色代表每个段的不同斜率/梯度百分比。

我以前的代码,我已经设法得到“山”的轮廓,现在进一步改进它,这样我现在正在制作一组代表每个颜色段的“框”。但是我遇到了一些问题。

这是我现在正在使用的代码。

    let myBezier = UIBezierPath()
    let nextPt = fillSlopes(at: idx)

    if idx > 0 {
      let prevPt = fillSlopes(at: idx - 1)
      myBezier.move(to: CGPoint(x: prevPt.x, y: height))
      myBezier.addLine(to: CGPoint(x: prevPt.x, y: prevPt.y))
      myBezier.addLine(to: CGPoint(x: nextPt.x, y: nextPt.y))
      myBezier.addLine(to: CGPoint(x: nextPt.x, y: height))
      myBezier.addLine(to: CGPoint(x: prevPt.x, y: height))

      myBezier.close()
      // This is test code, actual code will compare current slope % 
      // (in an array) and then based on the slope %, will change
      // the colour accordingly.
      if idx % 2 == 0 {
        UIColor.systemRed.setFill()
        myBezier.fill()
      } else {
        UIColor.systemBlue.setFill()
        myBezier.fill()
      }

这是结果。不幸的是,颜色不正确。我错过了什么?

除此之外,画那么多小方块真的是吃掉了很多CPU个循环,如果有其他的建议也能得到想要的输出结果,不胜感激。谢谢

更新: 非常感谢@seaspell,我意识到发生了什么。我将我的 let myBezier = UIBezierPath() 放在 for 循环之外,因此我的 [UIBezierPath] 数组被破坏了。例如:

一切都错了:

func xxx { 
  let myBezier = UIBezierPath() // <<<<<< this is wrong
      
  for idx in slopeBars.indices { 
      // do stuff 
  }

这是正确的,并且还解决了一些性能问题:

func xxx { 
  var slopeBars = [UIBezierPath]()
  var slopeBarColorArray = [UIColor]()

  for idx in slopeBars.indices { 
    let myBezier = UIBezierPath()  // <<<<< Moved here
    let nextPt = fillSlopes(at: idx)

    if idx > 0 {
      let prevPt = fillSlopes(at: idx - 1)
      myBezier.move(to: CGPoint(x: prevPt.x, y: height))
      myBezier.addLine(to: CGPoint(x: prevPt.x, y: prevPt.y))
      myBezier.addLine(to: CGPoint(x: nextPt.x, y: nextPt.y))
      myBezier.addLine(to: CGPoint(x: nextPt.x, y: height))
      myBezier.addLine(to: CGPoint(x: prevPt.x, y: height))

      myBezier.close()
      slopeBars.append(myBezier)

        if gradient < 0.0 {
          slopeBarColorArray.append(UIColor(cgColor: Consts.elevChart.slope0_0))
        } else if gradient < 4.0 {
          slopeBarColorArray.append(UIColor(cgColor: Consts.elevChart.slope0_4))
       }

    for i in 0..<slopeBars.count {
      if !slopeBarColorArray.isEmpty {
        slopeBarColorArray[i].setStroke()
        slopeBarColorArray[i].setFill()
      }
    
      slopeBars[i].stroke()
      slopeBars[i].fill()
    }
  }

为此使用贝塞尔曲线路径并没有什么错。不过,您需要为每个条形图使用一个新的条形图,而且您不想在 draw(rect:) 中构建它们。我怀疑这是 CPU 问题的原因。

看看我整理的这个例子。

   class BarChartView: UIView {

    lazy var dataPoints: [CGFloat] = {
        var points = [CGFloat]()
        for _ in 1...barCount {
            let random = arc4random_uniform(UInt32(bounds.maxY / 2))
            points.append(CGFloat(random))
        }
        return points
    }()

    var bars = [UIBezierPath]()
    private let barCount = 20

    lazy var colors: [UIColor] = {
        let colors:  [UIColor] = [.red, .blue, .cyan, .brown, .darkGray, .green, .yellow, .gray, .lightGray, .magenta, .orange, .purple, .systemPink, .white]
        var random = [UIColor]()

        for i in 0...barCount {
            random.append(colors[Int(arc4random_uniform(UInt32(colors.count)))])
        }

        return random
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.bars = buildBars()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        self.bars = buildBars()
    }


    func buildBars() -> [UIBezierPath] {

        let lineWidth = Int(bounds.maxX / CGFloat(barCount))

        var bars = [UIBezierPath]()

        for i in 0..<barCount {

            let line = UIBezierPath()

            let leftX = lineWidth * i
            let rightX = leftX + lineWidth
            let centerX = leftX + (lineWidth / 2)

            let nextLeftPoint = (i == 0 ? dataPoints[i] : dataPoints[i - 1])
            let nextRightPoint = (i == barCount - 1 ? dataPoints[i] : dataPoints[i + 1])

            let currentPoint = dataPoints[i]


            //bottom left
            line.move(to:  CGPoint(x: leftX, y: Int(bounds.maxY)) )
            //bottom right
            line.addLine(to: CGPoint(x: rightX, y: Int(bounds.maxY)) )

            //top right
            line.addLine(to: CGPoint(x: rightX,  y: Int((currentPoint + nextRightPoint) / 2)) )

            //top center
            line.addLine(to: CGPoint(x: centerX, y: Int(currentPoint)) )

            //top left
            line.addLine(to: CGPoint(x: leftX,  y: Int((currentPoint + nextLeftPoint) / 2)) )

            //close the path
            line.close()

            bars.append(line)
        }

        return bars
    }

    override func draw(_ rect: CGRect) {
        super.draw(rect)

        for i in 0..<bars.count {
            colors[i].setStroke()
            colors[i].setFill()

            bars[i].stroke()
            bars[i].fill()
        }
    }
}