超过阈值时如何更改UIBezierPath上的笔触和路径颜色

时间:2018-08-16 14:40:01

标签: ios swift uibezierpath

Core Graphics的新手,有一个问题,我不确定该如何处理。

我需要绘制一个图形,该图形在路径越过阈值线时会更改笔触和填充颜色,如本示例所示:

enter image description here

我可以毫无问题地将图形绘制成绿色。我不确定该怎么做是高于或低于阈值线的部分。路径可能会跨越点之间的阈值。

我曾想过两次绘制图形,首先是橙色,然后是绿色,然后在绿色路径上添加一个遮罩,其中阈值线形成一个矩形,以使图形的橙色版本能够显示出来。

我确定有更好的方法。任何指针将不胜感激。我正在使用Swift 4。

1 个答案:

答案 0 :(得分:2)

您在正确的轨道上。对于每个色带:

  1. 保存图形状态。
  2. 将上下文剪切到当前色带仅覆盖的区域。
  3. 用适当的颜色填充和描边整个路径。
  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()
    }

结果:

demo result

这是我完整的游乐场代码:

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: $0, y: cos($0)) }

PlaygroundPage.current.liveView = view