LoginSignup
1
2

More than 3 years have passed since last update.

Swift + Chartsでグリッド線表示のカスタマイズ

Posted at

目的

Chartsでグラフを描画する際、X軸のグリッド線の表示をカスタマイズしたい。

ライブラリ

danielgindi/Charts

任意のxAxisRendererをセットして描画をカスタマイズ

BarLineChartViewBaseのxAxisRendererプロパティには以下のコメントが書かれています。


/// The X axis renderer. This is a read-write property so you can set your own custom renderer here.
    /// **default**: An instance of XAxisRenderer
    @objc open lazy var xAxisRenderer = XAxisRenderer(viewPortHandler: _viewPortHandler, xAxis: _xAxis, transformer: _leftAxisTransformer)

XAxisRendererクラスのrenderGridLinesファンクションがグリッド線表示を行う処理です。
これを継承したクラスでオーバーライドしてあげれば良いでしょう。

例えば偶数のときだけ表示したいというような場合とします。

イメージ

デフォルト 偶数だけ

カスタムクラスの作成

swift:CustomXAxisRender.swift

/// 呼び出し元で制御できるようプロトコルを作っておく
protocol CustomXAxisRenderDelegate {
    func isRender(entry:Double) -> Bool
}
/// Optional化するためのエクステンション
extension CustomXAxisRenderDelegate {
    func isRender(entry:Double) -> Bool{
        return true
    }
}

/// レンダラーのカスタムクラス
class CustomXAxisRender: XAxisRenderer {

    var renderDelegate:CustomXAxisRenderDelegate?

    override init(viewPortHandler: ViewPortHandler, xAxis: XAxis?, transformer: Transformer?) {
        super.init(viewPortHandler: viewPortHandler, xAxis: xAxis, transformer: transformer)
    }

    open override func renderGridLines(context: CGContext)
    {
        guard
            let xAxis = self.axis as? XAxis,
            let transformer = self.transformer
            else { return }

        if !xAxis.isDrawGridLinesEnabled || !xAxis.isEnabled
        {
            return
        }

        context.saveGState()
        defer { context.restoreGState() }
        context.clip(to: self.gridClippingRect)

        context.setShouldAntialias(xAxis.gridAntialiasEnabled)
        context.setStrokeColor(xAxis.gridColor.cgColor)
        context.setLineWidth(xAxis.gridLineWidth)
        context.setLineCap(xAxis.gridLineCap)

        if xAxis.gridLineDashLengths != nil
        {
            context.setLineDash(phase: xAxis.gridLineDashPhase, lengths: xAxis.gridLineDashLengths)
        }
        else
        {
            context.setLineDash(phase: 0.0, lengths: [])
        }

        let valueToPixelMatrix = transformer.valueToPixelMatrix

        var position = CGPoint(x: 0.0, y: 0.0)

        let entries = xAxis.entries




        for i in stride(from: 0, to: entries.count, by: 1)
        {
            /// 元の処理からの変更点はここだけ
            /// 引数にグラフメモリの値をセットしてあげる
            if let delegate = self.renderDelegate {
                /// entriesにグリッド線の値が入っている
                if !delegate.isRender(entry:entries[i]) {
                    continue
                }
            }

            position.x = CGFloat(entries[i])
            position.y = position.x
            position = position.applying(valueToPixelMatrix)

            drawGridLine(context: context, x: position.x, y: position.y)
        }
    }
}

XAxisRendererクラスにデリゲートを追加し、その戻り値で描画有無を制御しているだけです。
これを使うViewControllerとしては

ViewController.swift

class ViewController: UIViewController,CustomXAxisRenderDelegate  {

    var chartView:LineChartView!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.makeChart()
    }


    private func makeChart() {
        self.chartView = LineChartView(frame: self.view.frame)
        /// カスタムレンダラーをインスタンス化。コンストラクタの値はChartViewや元のXAxisのを再利用
        var customRender = CustomXAxisRender(viewPortHandler: self.chartView.viewPortHandler,
                                              xAxis: self.chartView.xAxis,
                                              transformer: self.chartView.xAxisRenderer.transformer!)
        /// デリゲートをセット
        customRender.renderDelegate = self
        /// カスタムレンダラーをセットする
        self.chartView.xAxisRenderer = customRender

        let xAxis = self.chartView.xAxis
        xAxis.labelPosition = .bottom

        chartView.data = self.createChartsData()
        self.view.addSubview(chartView)

    }

    /// チャートデータのセットなど
    private func createChartsData() -> LineChartData {
        let bellValues = [120,109,86,214,487,64,87]
        let day = [1,2,3,4,5,6,7]

        var datas:[ChartDataEntry] = []

        for i in 0..<day.count {
            datas.append(ChartDataEntry(x: Double(day[i]), y: Double(bellValues[i])))
        }

        var lineChart = LineChartDataSet(entries: datas)

        return LineChartData(dataSets: [lineChart])
    }

    /// グリッド線描画毎に呼ばれるデリゲート
    func isRender(entry:Double) -> Bool{
        return Int(entry.truncatingRemainder(dividingBy: 2.0)) == 0
    }

}

サンプルソースはこちらに上げています

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2