超过阈值时如何更改 UIBezierPath 上的笔划和路径颜色
How to change stroke and path color on UIBezierPath when it crosses a threshold
Core Graphics 的新手,有一个问题我不知道如何解决。
我需要绘制一个图形,当路径穿过阈值线时会改变描边和填充颜色,如本例所示:
我可以毫无问题地将图表绘制成全绿色。我不确定如何处理高于或低于阈值线的部分。该路径可能会在两点之间穿过阈值。
我想过两次绘制图表,首先是橙色,然后是绿色,然后在绿色路径上添加一个遮罩,阈值线形成一个矩形,允许橙色版本的图表显示出来。
我确信有更好的方法。任何指针将不胜感激。我正在使用 Swift 4.
你走在正确的轨道上。对于每个色带:
- 保存图形状态。
- 将上下文剪切到当前色带覆盖的区域。
- 用适当的颜色填充和描边完整路径。
- 恢复图形状态。
在代码中,它可能看起来像这样:
for band in bands {
let y0 = max(CGPoint(x: 0, y: band.min).applying(transform).y, 0)
let y1 = min(CGPoint(x: 0, y: band.max).applying(transform).y, mySize.height)
gc.saveGState(); do {
gc.clip(to: CGRect(x: 0, y: y0, width: mySize.width, height: y1 - y0))
band.fillColor.setFill()
gc.addPath(pathForFilling)
gc.fillPath()
band.strokeColor.setStroke()
gc.addPath(pathForStroking)
gc.strokePath()
}; gc.restoreGState()
}
结果:
这是我的完整 playground 代码:
import UIKit
class BandedGraphView: UIView {
struct Band {
var min: CGFloat // In data geometry
var max: CGFloat // In data geometry
var strokeColor: UIColor
var fillColor: UIColor { return strokeColor.withAlphaComponent(0.2) }
}
var bands: [Band] = [] {
didSet { setNeedsDisplay() }
}
/// The minimum visible data geometry coordinate
var minVisiblePoint = CGPoint.zero {
didSet { setNeedsDisplay() }
}
/// The maximum visible data geometry coordinate
var maxVisiblePoint = CGPoint(x: 1, y: 1) {
didSet { setNeedsDisplay() }
}
/// Data points, in data geometry.
var data: [CGPoint] = [] {
didSet { setNeedsDisplay() }
}
var lineWidth: CGFloat = 2 {
didSet { setNeedsDisplay() }
}
override func draw(_ rect: CGRect) {
guard
minVisiblePoint.x != maxVisiblePoint.x,
minVisiblePoint.y != maxVisiblePoint.y,
!bands.isEmpty,
!data.isEmpty,
let gc = UIGraphicsGetCurrentContext()
else { return }
let mySize = bounds.size
var transform = CGAffineTransform.identity
transform = transform.scaledBy(x: mySize.width / (maxVisiblePoint.x - minVisiblePoint.x), y: mySize.height / (maxVisiblePoint.y - minVisiblePoint.y))
transform = transform.translatedBy(x: -minVisiblePoint.x, y: -minVisiblePoint.y)
let pathForStroking = CGMutablePath()
pathForStroking.addLines(between: data, transform: transform)
let pathForFilling = pathForStroking.mutableCopy(using: nil)!
let firstPoint = data.first!.applying(transform)
let lastPoint = data.last!.applying(transform)
print(pathForFilling)
pathForFilling.addLine(to: CGPoint(x: lastPoint.x, y: -CGFloat.greatestFiniteMagnitude))
pathForFilling.addLine(to: CGPoint(x: firstPoint.x, y: -CGFloat.greatestFiniteMagnitude))
pathForFilling.closeSubpath()
print(pathForFilling)
// Transform the context so the origin is at the lower left.
gc.translateBy(x: 0, y: mySize.height)
gc.scaleBy(x: 1, y: -1)
for band in bands {
let y0 = max(CGPoint(x: 0, y: band.min).applying(transform).y, 0)
let y1 = min(CGPoint(x: 0, y: band.max).applying(transform).y, mySize.height)
gc.saveGState(); do {
gc.clip(to: CGRect(x: 0, y: y0, width: mySize.width, height: y1 - y0))
band.fillColor.setFill()
gc.addPath(pathForFilling)
gc.fillPath()
band.strokeColor.setStroke()
gc.addPath(pathForStroking)
gc.strokePath()
}; gc.restoreGState()
}
}
}
import PlaygroundSupport
let view = BandedGraphView(frame: CGRect(x: 0, y: 0, width: 400, height: 300))
view.backgroundColor = .white
view.bands = [
.init(min: -CGFloat.infinity, max: -0.8, strokeColor: .blue),
.init(min: -0.8, max: 0.2, strokeColor: .red),
.init(min: 0.2, max: CGFloat.infinity, strokeColor: .orange),
]
view.minVisiblePoint = CGPoint(x: 0, y: -2)
view.maxVisiblePoint = CGPoint(x: 10, y: 2)
view.lineWidth = 2
view.data = stride(from: CGFloat(0), through: CGFloat(10), by: CGFloat(0.01)).map { CGPoint(x: [=11=], y: cos([=11=])) }
PlaygroundPage.current.liveView = view
Core Graphics 的新手,有一个问题我不知道如何解决。
我需要绘制一个图形,当路径穿过阈值线时会改变描边和填充颜色,如本例所示:
我可以毫无问题地将图表绘制成全绿色。我不确定如何处理高于或低于阈值线的部分。该路径可能会在两点之间穿过阈值。
我想过两次绘制图表,首先是橙色,然后是绿色,然后在绿色路径上添加一个遮罩,阈值线形成一个矩形,允许橙色版本的图表显示出来。
我确信有更好的方法。任何指针将不胜感激。我正在使用 Swift 4.
你走在正确的轨道上。对于每个色带:
- 保存图形状态。
- 将上下文剪切到当前色带覆盖的区域。
- 用适当的颜色填充和描边完整路径。
- 恢复图形状态。
在代码中,它可能看起来像这样:
for band in bands {
let y0 = max(CGPoint(x: 0, y: band.min).applying(transform).y, 0)
let y1 = min(CGPoint(x: 0, y: band.max).applying(transform).y, mySize.height)
gc.saveGState(); do {
gc.clip(to: CGRect(x: 0, y: y0, width: mySize.width, height: y1 - y0))
band.fillColor.setFill()
gc.addPath(pathForFilling)
gc.fillPath()
band.strokeColor.setStroke()
gc.addPath(pathForStroking)
gc.strokePath()
}; gc.restoreGState()
}
结果:
这是我的完整 playground 代码:
import UIKit
class BandedGraphView: UIView {
struct Band {
var min: CGFloat // In data geometry
var max: CGFloat // In data geometry
var strokeColor: UIColor
var fillColor: UIColor { return strokeColor.withAlphaComponent(0.2) }
}
var bands: [Band] = [] {
didSet { setNeedsDisplay() }
}
/// The minimum visible data geometry coordinate
var minVisiblePoint = CGPoint.zero {
didSet { setNeedsDisplay() }
}
/// The maximum visible data geometry coordinate
var maxVisiblePoint = CGPoint(x: 1, y: 1) {
didSet { setNeedsDisplay() }
}
/// Data points, in data geometry.
var data: [CGPoint] = [] {
didSet { setNeedsDisplay() }
}
var lineWidth: CGFloat = 2 {
didSet { setNeedsDisplay() }
}
override func draw(_ rect: CGRect) {
guard
minVisiblePoint.x != maxVisiblePoint.x,
minVisiblePoint.y != maxVisiblePoint.y,
!bands.isEmpty,
!data.isEmpty,
let gc = UIGraphicsGetCurrentContext()
else { return }
let mySize = bounds.size
var transform = CGAffineTransform.identity
transform = transform.scaledBy(x: mySize.width / (maxVisiblePoint.x - minVisiblePoint.x), y: mySize.height / (maxVisiblePoint.y - minVisiblePoint.y))
transform = transform.translatedBy(x: -minVisiblePoint.x, y: -minVisiblePoint.y)
let pathForStroking = CGMutablePath()
pathForStroking.addLines(between: data, transform: transform)
let pathForFilling = pathForStroking.mutableCopy(using: nil)!
let firstPoint = data.first!.applying(transform)
let lastPoint = data.last!.applying(transform)
print(pathForFilling)
pathForFilling.addLine(to: CGPoint(x: lastPoint.x, y: -CGFloat.greatestFiniteMagnitude))
pathForFilling.addLine(to: CGPoint(x: firstPoint.x, y: -CGFloat.greatestFiniteMagnitude))
pathForFilling.closeSubpath()
print(pathForFilling)
// Transform the context so the origin is at the lower left.
gc.translateBy(x: 0, y: mySize.height)
gc.scaleBy(x: 1, y: -1)
for band in bands {
let y0 = max(CGPoint(x: 0, y: band.min).applying(transform).y, 0)
let y1 = min(CGPoint(x: 0, y: band.max).applying(transform).y, mySize.height)
gc.saveGState(); do {
gc.clip(to: CGRect(x: 0, y: y0, width: mySize.width, height: y1 - y0))
band.fillColor.setFill()
gc.addPath(pathForFilling)
gc.fillPath()
band.strokeColor.setStroke()
gc.addPath(pathForStroking)
gc.strokePath()
}; gc.restoreGState()
}
}
}
import PlaygroundSupport
let view = BandedGraphView(frame: CGRect(x: 0, y: 0, width: 400, height: 300))
view.backgroundColor = .white
view.bands = [
.init(min: -CGFloat.infinity, max: -0.8, strokeColor: .blue),
.init(min: -0.8, max: 0.2, strokeColor: .red),
.init(min: 0.2, max: CGFloat.infinity, strokeColor: .orange),
]
view.minVisiblePoint = CGPoint(x: 0, y: -2)
view.maxVisiblePoint = CGPoint(x: 10, y: 2)
view.lineWidth = 2
view.data = stride(from: CGFloat(0), through: CGFloat(10), by: CGFloat(0.01)).map { CGPoint(x: [=11=], y: cos([=11=])) }
PlaygroundPage.current.liveView = view