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

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

## ライブラリ

[danielgindi/Charts](https://github.com/danielgindi/Charts)


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

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

```swift

/// 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ファンクションがグリッド線表示を行う処理です。
これを継承したクラスでオーバーライドしてあげれば良いでしょう。

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

イメージ

| デフォルト |  | 偶数だけ |
|:-:|:-:|:-:|
|  <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59457/08927359-bf04-6801-cd45-aba52fd4f999.png" width=200px /> | → | <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59457/a6442d11-4777-a0c9-7692-854ecc3f706e.png" width=200px />  |

カスタムクラスの作成

```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としては

```swift: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
    }
    
}

```

[サンプルソースはこちらに上げています](https://github.com/onizine/ChartsCustomize)