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()
}
}
}
我的目标是创建一个类似于下图的图表。其中差异颜色代表每个段的不同斜率/梯度百分比。
我以前的代码,我已经设法得到“山”的轮廓,现在进一步改进它,这样我现在正在制作一组代表每个颜色段的“框”。但是我遇到了一些问题。
这是我现在正在使用的代码。
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()
}
}
}