Core-Plotをじっくり使ってみる(その3)
昨日で X,Y軸とかグラフの外枠の部分はできた。
今日はグラフの中身を作って最初のサンプルは終了。
プロットを設定する
Core-Plot では、棒グラフの棒や線グラフの線などのグラフの中身は CPTPlot というクラスになる。で、棒グラフや線グラフ用に専用のクラスが用意されてる。
棒グラフ | 円グラフ | 線グラフ |
---|---|---|
CPTBarPlot | CPTPieChart | CPTScatterPlot |
範囲グラフ? | 売買グラフ? | |
CPTRangePlot | CPTTradingRangePlot | |
範囲グラフ?と売買グラフ?は日本語でなんて言うのか知らないのだけど、まぁ上記5種類が用意されている。
線グラフは線を引かなければ散布図になるし、これら5種類で大体のグラフは描けると思う。
今回は棒グラフなので CPTBarPlot を使う。
特に何も設定しないとこんな感じ。
// First bar plot CPTBarPlot *barPlot = [CPTBarPlot tubularBarPlotWithColor:[CPTColor redColor] horizontalBars:NO]; barPlot.baseValue = CPTDecimalFromString(@"0"); barPlot.dataSource = self; barPlot.identifier = @"Bar Plot 1"; [graph addPlot:barPlot toPlotSpace:plotSpace];
やっていることは…
- 棒の色を指定して棒グラフ(CPTBarPlot)を作る。
- X軸の値の基本になる値は 0。これを変更すると棒グラフの棒が上下に動く。
- データソースは自分自身(ViewController)とする。
- この棒グラフの id は Bar Plot 1 とする。
- で、この棒グラフ(CPTBarPlot)をグラフ(graph)のプロットスペースに加える。ここで出てくるプロットスペースは、前に取り出した graph.defaultPlotSpace。
ところで、データソースは自分自身としたのだけど、まだ CPTPlotDataSource プロトコルのメソッドを用意していないので、実際にはこのままだとエラーになる。
2011-08-27 23:38:14.575 MyBarGraph[11952:b303] -[MyBarGraphViewController numberOfRecordsForPlot:]: unrecognized selector sent to instance 0x4c8c790 2011-08-27 23:38:14.578 MyBarGraph[11952:b303] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MyBarGraphViewController numberOfRecordsForPlot:]: unrecognized selector sent to instance 0x4c8c790'
エラーにならないようにするために、CPTPlotDataSource プロトコルのメソッドを用意しよう。
CPTPlotDataSource プロトコル対応
CPTPlotDataSource プロトコルに対応するには、まずこれが必ず必要
(NSUInteger) - numberOfRecordsForPlot:
この部分は今回はこんな感じ。
-(NSUInteger)numberOfRecordsForPlot:(CPTPlot *)plot { return 5; }
単純にグラフには何個レコードがありますか?という確認なので、今回は決め打ちで 5個にしてある。
実際に使う場合には、例えば横軸が日付になるなら何日分か?を返すとかすれば良い。
で、次に必要なのは以下の中のどれか1つ
(NSArray *) - numbersForPlot:field:recordIndexRange: (NSNumber *) - numberForPlot:field:recordIndex: (double *) - doublesForPlot:field:recordIndexRange: (double) - doubleForPlot:field:recordIndex: (CPTNumericData *) - dataForPlot:field:recordIndexRange:
今回は (NSNumber *) - numberForPlot:field:recordIndex: を使用してみてこんな感じに。
-(NSNumber *)numberForPlot:(CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index { if(fieldEnum == CPTBarPlotFieldBarLocation) return [NSNumber numberWithDouble:index+1]; else { if(plot.identifier == @"Bar Plot 1") return [NSNumber numberWithDouble:(index+1)*2.0f]; else return 0; } }
まず、(NSNumber *) - numberForPlot:field:recordIndex: は、そのプロット(CPTPlot)の field の recordIndex 番目のレコードの値を返すというメソッド。
今回は CPTPlot が棒グラフ1つしかないから CPTPlot の確認は行ってない。
けど、もしも複数の CPTPlot を乗せるなら確認が必要になる。
次の field はプロットのどの部分の値が必要なのかを示している。
これはそれぞれの CPTPlot(CPTBarPlot や CPTScatterPlot 等)で用意されていて、CPTBarPlot の場合はこんなのがある。
typedef enum _CPTBarPlotField { CPTBarPlotFieldBarLocation = 2, ///< Bar location on independent coordinate axis. CPTBarPlotFieldBarTip = 3, ///< Bar tip value. CPTBarPlotFieldBarBase = 4 ///< Bar base (used only if barBasesVary is YES). } CPTBarPlotField;
そして CPTScatterPlot ではこんなんがある。
typedef enum _CPTScatterPlotField { CPTScatterPlotFieldX, ///< X values. CPTScatterPlotFieldY ///< Y values. } CPTScatterPlotField;
今回の CPTBarPlot の場合は CPTBarPlotFieldBarLocation が来たら「そのレコードは何番目の棒か?」を返せば良い。
今回は recordIndex の値に 1 をプラスしている。これは最初のレコードを 0番目でなく 1番目の場所から描く様にしたため。
ちなみに 1を加えずにそのまま index を返すとこんな感じのグラフになる。
最初の棒の位置が 0なので、Y軸に重なっちゃう…。
これ、Y軸の交差する位置を変えたり、棒を描く位置をずらしたり、方法は色々とあるのだけど、今回はとりあえずこんな感じで。
今回は CPTBarPlotFieldBarLocation でなければ実際のレコードの値の問い合わせなので、(index+1)*2.0f で右肩上がりの棒グラフになるようにしている。
で、これを最初に示したサンプル(+Y軸の目盛線)にするにはもう少しだけ修正をしている。
// First bar plot CPTBarPlot *barPlot = [CPTBarPlot tubularBarPlotWithColor:[CPTColor redColor] horizontalBars:NO]; barPlot.baseValue = CPTDecimalFromString(@"0"); barPlot.dataSource = self; barPlot.barWidth = CPTDecimalFromFloat(1.0f); barPlot.identifier = @"Bar Plot 1"; barPlot.barOffset = CPTDecimalFromString(@"3"); [graph addPlot:barPlot toPlotSpace:plotSpace];
- barPlot.barWidth = CPTDecimalFromFloat(1.0f); で棒の幅を1にしている。これで棒の間の隙間が無くなった。
- barPlot.barOffset = CPTDecimalFromString(@"3"); で描く位置を右に3ずらしている。小数点でもいけるので 0.5 だと棒の半分ずれる。
取り敢えず簡単な棒グラフならこれで作れるようになったはず!
UITableView みたいに datasource に値を問い合わせてくるので、CoreData を使ってグラフを描くのは結構簡単。
というわけで、明日は X軸を日付にしてみる!