2017-11-03 9 views
0

私はSwift Chartを使用しています。私は、ユーザーが範囲を選択できるように変更したいと思います。このアイデアは、タッチしたり、左右にスワイプしたり、指を持ち上げたりすることです。スワイプされた領域を強調表示し、スワイプの開始値と終了値を取得する方法を提供する必要があります。 touchesBegan()touchesEnded()のイベントを変更する必要があると思いますが、どうすればよいか分かりません。SwiftChart Add範囲の強調表示

+0

試したコード、予想される出力、および現在使用しているスウィフトチャートモジュールを表示しますか? –

答えて

0

ここで私はこの仕事をするためにやったことだ:

私はtouchesBegan()

leftRangePoint = touches.first! 
leftRangeLocation = leftRangePoint.location(in: self).x 

を変更し、touchesEndedにルーチンを追加したクラスに

// Range selection 
open var leftRangePoint: UITouch! 
open var rightRangePoint: UITouch! 
open var leftRangeLocation: CGFloat = 0 
open var rightRangeLocation: CGFloat = 0 

を範囲選択変数を追加しました()

handleRangeTouchesEnded(touches, event: event) 

完全なコードは次のとおりです。

// Chart.swift 
// 
// Created by Giampaolo Bellavite on 07/11/14. 
// Copyright (c) 2014 Giampaolo Bellavite. All rights reserved. 
import UIKit 

public protocol ChartDelegate: class { 
    func didTouchChart(_ chart: Chart, indexes: [Int?], x: Float, left: CGFloat) 
    func didFinishTouchingChart(_ chart: Chart) 
    func didEndTouchingChart(_ chart: Chart) 
} 

typealias ChartPoint = (x: Float, y: Float) 
public enum ChartLabelOrientation { 
    case horizontal 
    case vertical 
} 

@IBDesignable open class Chart: UIControl { 
    @IBInspectable 
    open var identifier: String? 
    open var series: [ChartSeries] = [] { 
     didSet { 
      setNeedsDisplay() 
     } 
    } 

    open var xLabels: [Float]? 
    open var xLabelsFormatter = { (labelIndex: Int, labelValue: Float) -> String in 
     String(Int(labelValue)) 
    } 

    open var xLabelsTextAlignment: NSTextAlignment = .left 
    open var xLabelsOrientation: ChartLabelOrientation = .horizontal 
    open var xLabelsSkipLast: Bool = true 
    open var xLabelsSkipAll: Bool = true 
    open var yLabels: [Float]? 
    open var yLabelsFormatter = { (labelIndex: Int, labelValue: Float) -> String in 
     String(Int(labelValue)) 
    } 

    open var yLabelsOnRightSide: Bool = false 
    open var labelFont: UIFont? = UIFont.systemFont(ofSize: 12) 

    @IBInspectable 
    open var labelColor: UIColor = UIColor.black 

    @IBInspectable 
    open var axesColor: UIColor = UIColor.gray.withAlphaComponent(0.3) 

    @IBInspectable 
    open var gridColor: UIColor = UIColor.gray.withAlphaComponent(0.3) 
    open var showXLabelsAndGrid: Bool = true 
    open var showYLabelsAndGrid: Bool = true 
    open var bottomInset: CGFloat = 20 
    open var topInset: CGFloat = 20 

    @IBInspectable 
    open var lineWidth: CGFloat = 2 

    weak open var delegate: ChartDelegate? 

    open var minX: Float? 
    open var minY: Float? 
    open var maxX: Float? 
    open var maxY: Float? 
    open var highlightLineColor = UIColor.gray 
    open var highlightLineWidth: CGFloat = 0.5 
    open var areaAlphaComponent: CGFloat = 0.1 
    open var leftRangePoint: UITouch! 
    open var rightRangePoint: UITouch! 
    open var leftRangeLocation: CGFloat = 0 
    open var rightRangeLocation: CGFloat = 0 

    fileprivate var highlightShapeLayer: CAShapeLayer! 
    fileprivate var layerStore: [CAShapeLayer] = [] 

    fileprivate var drawingHeight: CGFloat! 
    fileprivate var drawingWidth: CGFloat! 

    fileprivate var min: ChartPoint! 
    fileprivate var max: ChartPoint! 

    typealias ChartLineSegment = [ChartPoint] 

    override public init(frame: CGRect) { 
     super.init(frame: frame) 
     commonInit() 
    } 

    required public init?(coder aDecoder: NSCoder) { 
     super.init(coder: aDecoder) 
     commonInit() 
    } 

    convenience public init() { 
     self.init(frame: .zero) 
     commonInit() 
    } 

    private func commonInit() { 
     backgroundColor = UIColor.clear 
     contentMode = .redraw // redraw rects on bounds change 
    } 

    override open func draw(_ rect: CGRect) { 
     #if TARGET_INTERFACE_BUILDER 
      drawIBPlaceholder() 
      #else 
      drawChart() 
     #endif 
    } 

    open func add(_ series: ChartSeries) { 
     self.series.append(series) 
    } 

    open func add(_ series: [ChartSeries]) { 
     for s in series { 
      add(s) 
     } 
    } 

    open func removeSeriesAt(_ index: Int) { 
     series.remove(at: index) 
    } 

    open func removeAllSeries() { 
     series = [] 
    } 

    open func valueForSeries(_ seriesIndex: Int, atIndex dataIndex: Int?) -> Float? { 
     if dataIndex == nil { return nil } 
     let series = self.series[seriesIndex] as ChartSeries 
     return series.data[dataIndex!].y 
    } 

    fileprivate func drawIBPlaceholder() { 
     let placeholder = UIView(frame: self.frame) 
     placeholder.backgroundColor = UIColor(red: 0.93, green: 0.93, blue: 0.93, alpha: 1) 
     let label = UILabel() 
     label.text = "Chart" 
     label.font = UIFont.systemFont(ofSize: 28) 
     label.textColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.2) 
     label.sizeToFit() 
     label.frame.origin.x += frame.width/2 - (label.frame.width/2) 
     label.frame.origin.y += frame.height/2 - (label.frame.height/2) 

     placeholder.addSubview(label) 
     addSubview(placeholder) 
    } 

    fileprivate func drawChart() { 
     drawingHeight = bounds.height - bottomInset - topInset 
     drawingWidth = bounds.width 

     let minMax = getMinMax() 
     min = minMax.min 
     max = minMax.max 

     highlightShapeLayer = nil 

     // Remove things before drawing, e.g. when changing orientation 
     for view in self.subviews { 
      view.removeFromSuperview() 
     } 
     for layer in layerStore { 
      layer.removeFromSuperlayer() 
     } 
     layerStore.removeAll() 

     // Draw content 
     for (index, series) in self.series.enumerated() { 
      // Separate each line in multiple segments over and below the x axis 
      let segments = Chart.segmentLine(series.data as ChartLineSegment, zeroLevel: series.colors.zeroLevel) 

      segments.forEach({ segment in 
       let scaledXValues = scaleValuesOnXAxis(segment.map({ return $0.x })) 
       let scaledYValues = scaleValuesOnYAxis(segment.map({ return $0.y })) 

       if series.line { 
        drawLine(scaledXValues, yValues: scaledYValues, seriesIndex: index) 
       } 
       if series.area { 
        drawArea(scaledXValues, yValues: scaledYValues, seriesIndex: index) 
       } 
      }) 
     } 

     drawAxes() 

     if showXLabelsAndGrid && (xLabels != nil || series.count > 0) { 
      drawLabelsAndGridOnXAxis() 
     } 
     if showYLabelsAndGrid && (yLabels != nil || series.count > 0) { 
      drawLabelsAndGridOnYAxis() 
     } 
    } 

    fileprivate func getMinMax() -> (min: ChartPoint, max: ChartPoint) { 
     // Start with user-provided values 
     var min = (x: minX, y: minY) 
     var max = (x: maxX, y: maxY) 

     // Check in datasets 
     for series in self.series { 
      let xValues = series.data.map({ (point: ChartPoint) -> Float in 
       return point.x }) 
      let yValues = series.data.map({ (point: ChartPoint) -> Float in 
       return point.y }) 

      let newMinX = xValues.min()! 
      let newMinY = yValues.min()! 
      let newMaxX = xValues.max()! 
      let newMaxY = yValues.max()! 

      if min.x == nil || newMinX < min.x! { min.x = newMinX } 
      if min.y == nil || newMinY < min.y! { min.y = newMinY } 
      if max.x == nil || newMaxX > max.x! { max.x = newMaxX } 
      if max.y == nil || newMaxY > max.y! { max.y = newMaxY } 
     } 

     // Check in labels 
     if xLabels != nil { 
      let newMinX = (xLabels!).min()! 
      let newMaxX = (xLabels!).max()! 
      if min.x == nil || newMinX < min.x! { min.x = newMinX } 
      if max.x == nil || newMaxX > max.x! { max.x = newMaxX } 
     } 

     if yLabels != nil { 
      let newMinY = (yLabels!).min()! 
      let newMaxY = (yLabels!).max()! 
      if min.y == nil || newMinY < min.y! { min.y = newMinY } 
      if max.y == nil || newMaxY > max.y! { max.y = newMaxY } 
     } 

     if min.x == nil { min.x = 0 } 
     if min.y == nil { min.y = 0 } 
     if max.x == nil { max.x = 0 } 
     if max.y == nil { max.y = 0 } 

     return (min: (x: min.x!, y: min.y!), max: (x: max.x!, max.y!)) 
    } 

    fileprivate func scaleValuesOnXAxis(_ values: [Float]) -> [Float] { 
     let width = Float(drawingWidth) 

     var factor: Float 
     if max.x - min.x == 0 { 
      factor = 0 
     } else { 
      factor = width/(max.x - min.x) 
     } 

     let scaled = values.map { factor * ($0 - self.min.x) } 
     return scaled 
    } 

    fileprivate func scaleValuesOnYAxis(_ values: [Float]) -> [Float] { 
     let height = Float(drawingHeight) 
     var factor: Float 
     if max.y - min.y == 0 { 
      factor = 0 
     } else { 
      factor = height/(max.y - min.y) 
     } 

     let scaled = values.map { Float(self.topInset) + height - factor * ($0 - self.min.y) } 
     return scaled 
    } 

    fileprivate func scaleValueOnYAxis(_ value: Float) -> Float { 
     let height = Float(drawingHeight) 
     var factor: Float 
     if max.y - min.y == 0 { 
      factor = 0 
     } else { 
      factor = height/(max.y - min.y) 
     } 

     let scaled = Float(self.topInset) + height - factor * (value - min.y) 
     return scaled 
    } 

    fileprivate func getZeroValueOnYAxis(zeroLevel: Float) -> Float { 
     if min.y > zeroLevel { 
      return scaleValueOnYAxis(min.y) 
     } else { 
      return scaleValueOnYAxis(zeroLevel) 
     } 
    } 

    fileprivate func drawLine(_ xValues: [Float], yValues: [Float], seriesIndex: Int) { 
     // YValues are "reverted" from top to bottom, so 'above' means <= level 
     let isAboveZeroLine = yValues.max()! <= self.scaleValueOnYAxis(series[seriesIndex].colors.zeroLevel) 
     let path = CGMutablePath() 
     path.move(to: CGPoint(x: CGFloat(xValues.first!), y: CGFloat(yValues.first!))) 
     for i in 1..<yValues.count { 
      let y = yValues[i] 
      path.addLine(to: CGPoint(x: CGFloat(xValues[i]), y: CGFloat(y))) 
     } 

     let lineLayer = CAShapeLayer() 
     lineLayer.frame = self.bounds 
     lineLayer.path = path 

     if isAboveZeroLine { 
      lineLayer.strokeColor = series[seriesIndex].colors.above.cgColor 
     } else { 
      lineLayer.strokeColor = series[seriesIndex].colors.below.cgColor 
     } 
     lineLayer.fillColor = nil 
     lineLayer.lineWidth = lineWidth 
     lineLayer.lineJoin = kCALineJoinBevel 

     self.layer.addSublayer(lineLayer) 

     layerStore.append(lineLayer) 
    } 

    fileprivate func drawArea(_ xValues: [Float], yValues: [Float], seriesIndex: Int) { 
     // YValues are "reverted" from top to bottom, so 'above' means <= level 
     let isAboveZeroLine = yValues.max()! <= self.scaleValueOnYAxis(series[seriesIndex].colors.zeroLevel) 
     let area = CGMutablePath() 
     let zero = CGFloat(getZeroValueOnYAxis(zeroLevel: series[seriesIndex].colors.zeroLevel)) 

     area.move(to: CGPoint(x: CGFloat(xValues[0]), y: zero)) 
     for i in 0..<xValues.count { 
      area.addLine(to: CGPoint(x: CGFloat(xValues[i]), y: CGFloat(yValues[i]))) 
     } 
     area.addLine(to: CGPoint(x: CGFloat(xValues.last!), y: zero)) 
     let areaLayer = CAShapeLayer() 
     areaLayer.frame = self.bounds 
     areaLayer.path = area 
     areaLayer.strokeColor = nil 
     if isAboveZeroLine { 
      areaLayer.fillColor = series[seriesIndex].colors.above.withAlphaComponent(areaAlphaComponent).cgColor 
     } else { 
      areaLayer.fillColor = series[seriesIndex].colors.below.withAlphaComponent(areaAlphaComponent).cgColor 
     } 
     areaLayer.lineWidth = 0 

     self.layer.addSublayer(areaLayer) 

     layerStore.append(areaLayer) 
    } 

    fileprivate func drawAxes() { 
     let context = UIGraphicsGetCurrentContext()! 
     context.setStrokeColor(axesColor.cgColor) 
     context.setLineWidth(0.5) 

     // horizontal axis at the bottom 
     context.move(to: CGPoint(x: CGFloat(0), y: drawingHeight + topInset)) 
     context.addLine(to: CGPoint(x: CGFloat(drawingWidth), y: drawingHeight + topInset)) 
     context.strokePath() 

     // horizontal axis at the top 
     context.move(to: CGPoint(x: CGFloat(0), y: CGFloat(0))) 
     context.addLine(to: CGPoint(x: CGFloat(drawingWidth), y: CGFloat(0))) 
     context.strokePath() 

     // horizontal axis when y = 0 
     if min.y < 0 && max.y > 0 { 
      let y = CGFloat(getZeroValueOnYAxis(zeroLevel: 0)) 
      context.move(to: CGPoint(x: CGFloat(0), y: y)) 
      context.addLine(to: CGPoint(x: CGFloat(drawingWidth), y: y)) 
      context.strokePath() 
     } 

     // vertical axis on the left 
     context.move(to: CGPoint(x: CGFloat(0), y: CGFloat(0))) 
     context.addLine(to: CGPoint(x: CGFloat(0), y: drawingHeight + topInset)) 
     context.strokePath() 

     // vertical axis on the right 
     context.move(to: CGPoint(x: CGFloat(drawingWidth), y: CGFloat(0))) 
     context.addLine(to: CGPoint(x: CGFloat(drawingWidth), y: drawingHeight + topInset)) 
     context.strokePath() 
    } 

    fileprivate func drawLabelsAndGridOnXAxis() { 
     let context = UIGraphicsGetCurrentContext()! 
     context.setStrokeColor(gridColor.cgColor) 
     context.setLineWidth(0.5) 

     var labels: [Float] 
     if xLabels == nil { 
      // Use labels from the first series 
      labels = series[0].data.map({ (point: ChartPoint) -> Float in 
       return point.x }) 
     } else { 
      labels = xLabels! 
     } 

     let scaled = scaleValuesOnXAxis(labels) 
     let padding: CGFloat = 5 
     scaled.enumerated().forEach { (i, value) in 
      let x = CGFloat(value) 
      let isLastLabel = x == drawingWidth 

      // Add vertical grid for each label, except axes on the left and right 
      if x != 0 && x != drawingWidth { 
       context.move(to: CGPoint(x: x, y: CGFloat(0))) 

       if xLabelsSkipAll { 
        let height: CGFloat = bounds.height - 20.0 
        context.addLine(to: CGPoint(x: x, y: height)) 
       } else { 
        context.addLine(to: CGPoint(x: x, y: bounds.height)) 
       } 

       context.strokePath() 
      } 

      if (xLabelsSkipLast && isLastLabel) || xLabelsSkipAll { 
       // Do not add label at the most right position 
       return 
      } 

      // Add label 
      let label = UILabel(frame: CGRect(x: x, y: drawingHeight, width: 0, height: 0)) 
      label.font = labelFont 
      label.text = xLabelsFormatter(i, labels[i]) 
      label.textColor = labelColor 

      // Set label size 
      label.sizeToFit() 
      // Center label vertically 
      label.frame.origin.y += topInset 
      if xLabelsOrientation == .horizontal { 
       // Add left padding 
       label.frame.origin.y -= (label.frame.height - bottomInset)/2 
       label.frame.origin.x += padding 

       // Set label's text alignment 
       label.frame.size.width = (drawingWidth/CGFloat(labels.count)) - padding * 2 
       label.textAlignment = xLabelsTextAlignment 
      } else { 
       label.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi/2)) 

       // Adjust vertical position according to the label's height 
       label.frame.origin.y += label.frame.size.height/2 

       // Adjust horizontal position as the series line 
       label.frame.origin.x = x 
       if xLabelsTextAlignment == .center { 
        // Align horizontally in series 
        label.frame.origin.x += ((drawingWidth/CGFloat(labels.count))/2) - (label.frame.size.width/2) 
       } else { 
        // Give some space from the vertical line 
        label.frame.origin.x += padding 
       } 
      } 
      self.addSubview(label) 
     } 
    } 

    fileprivate func drawLabelsAndGridOnYAxis() { 
     let context = UIGraphicsGetCurrentContext()! 
     context.setStrokeColor(gridColor.cgColor) 
     context.setLineWidth(0.5) 

     var labels: [Float] 
     if yLabels == nil { 
      labels = [(min.y + max.y)/2, max.y] 
      if yLabelsOnRightSide || min.y != 0 { 
       labels.insert(min.y, at: 0) 
      } 
     } else { 
      labels = yLabels! 
     } 

     let scaled = scaleValuesOnYAxis(labels) 
     let padding: CGFloat = 5 
     let zero = CGFloat(getZeroValueOnYAxis(zeroLevel: 0)) 

     scaled.enumerated().forEach { (i, value) in 
      let y = CGFloat(value) 

      // Add horizontal grid for each label, but not over axes 
      if y != drawingHeight + topInset && y != zero { 
       context.move(to: CGPoint(x: CGFloat(0), y: y)) 
       context.addLine(to: CGPoint(x: self.bounds.width, y: y)) 
       if labels[i] != 0 { 
        // Horizontal grid for 0 is not dashed 
        context.setLineDash(phase: CGFloat(0), lengths: [CGFloat(5)]) 
       } else { 
        context.setLineDash(phase: CGFloat(0), lengths: []) 
       } 
       context.strokePath() 
      } 

      let label = UILabel(frame: CGRect(x: padding, y: y, width: 0, height: 0)) 
      label.font = labelFont 
      label.text = yLabelsFormatter(i, labels[i]) 
      label.textColor = labelColor 
      label.sizeToFit() 

      if yLabelsOnRightSide { 
       label.frame.origin.x = drawingWidth 
       label.frame.origin.x -= label.frame.width + padding 
      } 

      // Labels should be placed above the horizontal grid 
      label.frame.origin.y -= label.frame.height 

      self.addSubview(label) 
     } 
     UIGraphicsEndImageContext() 
    } 

    fileprivate func drawHighlightLineFromLeftPosition(_ left: CGFloat) { 
     if let shapeLayer = highlightShapeLayer { 
      // Use line already created 
      let path = CGMutablePath() 

      path.move(to: CGPoint(x: left, y: 0)) 
      path.addLine(to: CGPoint(x: left, y: drawingHeight + topInset)) 
      shapeLayer.path = path 
     } else { 
      // Create the line 
      let path = CGMutablePath() 

      path.move(to: CGPoint(x: left, y: CGFloat(0))) 
      path.addLine(to: CGPoint(x: left, y: drawingHeight + topInset)) 
      let shapeLayer = CAShapeLayer() 
      shapeLayer.frame = self.bounds 
      shapeLayer.path = path 
      shapeLayer.strokeColor = highlightLineColor.cgColor 
      shapeLayer.fillColor = nil 
      shapeLayer.lineWidth = highlightLineWidth 

      highlightShapeLayer = shapeLayer 
      layer.addSublayer(shapeLayer) 
      layerStore.append(shapeLayer) 
     } 
    } 

    func handleTouchEvents(_ touches: Set<UITouch>, event: UIEvent!) { 
     let point = touches.first! 
     let left = point.location(in: self).x 
     let x = valueFromPointAtX(left) 

     if left < 0 || left > (drawingWidth as CGFloat) { 
      // Remove highlight line at the end of the touch event 
      if let shapeLayer = highlightShapeLayer { 
       shapeLayer.path = nil 
      } 
      delegate?.didFinishTouchingChart(self) 
      return 
     } 

     drawHighlightLineFromLeftPosition(left) 

     if delegate == nil { 
      return 
     } 

     var indexes: [Int?] = [] 

     for series in self.series { 
      var index: Int? = nil 
      let xValues = series.data.map({ (point: ChartPoint) -> Float in 
       return point.x }) 
      let closest = Chart.findClosestInValues(xValues, forValue: x) 
      if closest.lowestIndex != nil && closest.highestIndex != nil { 
       // Consider valid only values on the right 
       index = closest.lowestIndex 
      } 
      indexes.append(index) 
     } 

     delegate!.didTouchChart(self, indexes: indexes, x: x, left: left) 
    } 

    override open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { 
     handleTouchEvents(touches, event: event) 

     leftRangePoint = touches.first! 
     leftRangeLocation = leftRangePoint.location(in: self).x 
    } 

    override open func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { 
     handleTouchEvents(touches, event: event) 
     delegate?.didEndTouchingChart(self) 

     handleRangeTouchesEnded(touches, event: event) 
    } 

    func handleRangeTouchesEnded(_ touches: Set<UITouch>, event: UIEvent!) { 
     rightRangePoint = touches.first! 
     rightRangeLocation = rightRangePoint.location(in: self).x 

     // Make sure left is actually to the left 
     if rightRangeLocation < leftRangeLocation { 
      let rangePoint = leftRangePoint 
      let rangeLocation = leftRangeLocation 
      leftRangePoint = rightRangePoint 
      leftRangeLocation = rightRangeLocation 
      rightRangePoint = rangePoint 
      rightRangeLocation = rangeLocation 
     } 

     // Highlight the range 
     let layer = CAShapeLayer() 
     let width = rightRangeLocation - leftRangeLocation 
     layer.path = UIBezierPath(rect: CGRect(x: leftRangeLocation, y: topInset, width: width, height: drawingHeight)).cgPath 

     layer.fillColor = UIColor.red.cgColor 
     layer.opacity = 0.3 
     self.layer.addSublayer(layer) 
    } 

    override open func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { 
     handleTouchEvents(touches, event: event) 
    } 

    fileprivate func valueFromPointAtX(_ x: CGFloat) -> Float { 
     let value = ((max.x-min.x)/Float(drawingWidth)) * Float(x) + min.x 
     return value 
    } 

    fileprivate func valueFromPointAtY(_ y: CGFloat) -> Float { 
     let value = ((max.y - min.y)/Float(drawingHeight)) * Float(y) + min.y 
     return -value 
    } 

    fileprivate class func findClosestInValues(_ values: [Float], 
     forValue value: Float 
) -> (
      lowestValue: Float?, 
      highestValue: Float?, 
      lowestIndex: Int?, 
      highestIndex: Int? 
     ) { 
     var lowestValue: Float?, highestValue: Float?, lowestIndex: Int?, highestIndex: Int? 

     values.enumerated().forEach { (i, currentValue) in 

      if currentValue <= value && (lowestValue == nil || lowestValue! < currentValue) { 
       lowestValue = currentValue 
       lowestIndex = i 
      } 
      if currentValue >= value && (highestValue == nil || highestValue! > currentValue) { 
       highestValue = currentValue 
       highestIndex = i 
      } 
     } 
     return (
      lowestValue: lowestValue, 
      highestValue: highestValue, 
      lowestIndex: lowestIndex, 
      highestIndex: highestIndex 
     ) 
    } 

    fileprivate class func segmentLine(_ line: ChartLineSegment, zeroLevel: Float) -> [ChartLineSegment] { 
     var segments: [ChartLineSegment] = [] 
     var segment: ChartLineSegment = [] 

     line.enumerated().forEach { (i, point) in 
      segment.append(point) 
      if i < line.count - 1 { 
       let nextPoint = line[i+1] 
       if point.y >= zeroLevel && nextPoint.y < zeroLevel || point.y < zeroLevel && nextPoint.y >= zeroLevel { 
        // The segment intersects zeroLevel, close the segment with the intersection point 
        let closingPoint = Chart.intersectionWithLevel(point, and: nextPoint, level: zeroLevel) 
        segment.append(closingPoint) 
        segments.append(segment) 
        // Start a new segment 
        segment = [closingPoint] 
       } 
      } else { 
       // End of the line 
       segments.append(segment) 
      } 
     } 
     return segments 
    } 

    fileprivate class func intersectionWithLevel(_ p1: ChartPoint, and p2: ChartPoint, level: Float) -> ChartPoint { 
     let dy1 = level - p1.y 
     let dy2 = level - p2.y 
     return (x: (p2.x * dy1 - p1.x * dy2)/(dy1 - dy2), y: level) 
    } 
} 
関連する問題