質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

388閲覧

Swiftのchartsの円グラフについてです。

Hayato1201

総合スコア220

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2018/09/19 09:28

ChartsのPieChartでグラフ内のvalueを表示しない方法はありますか?
PieChartのvalueは以下の様に追加しますが,実際に表示するときその値を表示しないで円だけを表示する事は可能でしょうか?

Swift

1PieChartDataEntry(value: 5, label: "")

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

前回書いたコードの流用ですが

swift

1 let entries = someArray.map{ element -> PieChartDataEntry in 2 let value = Double(element) 3 return PieChartDataEntry(value: value) 4 } 5 //entriesは[PieChartDataEntry] 6 let dataSet = PieChartDataSet(values: entries, label: "") 7 8 dataSet.drawValuesEnabled = false //←こいつ

これでOKです。

iOS-Chartsって割となんでも出来ちゃうので大好きなのですが、どの段階(entry -> dataSet -> data)でどのプロパティをセットできるのかがちょっと分かりにくいですよね。

あとは力業でIValueFormatterNSObjectを継承したクラスを作ってその中でstringForValue(_:entry:dataSetIndex:viewPortHandler:) -> Stringを実装する時に返り値を全部""にしちゃって、そのクラスをdataSet.valueFormatterに放り込むなんてのもいけます。前の質問でPieChartに空欄があるということだったのでこっちも使える可能性があるかと思い紹介してみました。

swift

1class CustomFormatter: NSObject, IValueFormatter { 2 func stringForValue(_ value: Double, 3 entry: ChartDataEntry, 4 dataSetIndex: Int, 5 viewPortHandler: ViewPortHandler?) -> String { 6 return "" 7 } 8} 9 10func setupPieChart() { 11 let entries = someArray.map{ element -> PieChartDataEntry in 12 let value = Double(element) 13 return PieChartDataEntry(value: value) 14 } 15 //entriesは[PieChartDataEntry] 16 let dataSet = PieChartDataSet(values: entries, label: "") 17 dataSet.valueFormatter = CustomFormatter() //<-ここ 18}

また、このやり方を応用すれば任意のEntryだけ好きな文字列を表示させたりも出来ます。

(追記および修正)

ご要望にお応えしてみました。
丸々コピペしても動きます(動作確認済み)。Outlet接続はちゃんと再設定してください。あとコンソールに色々printした結果が出てくるのでその結果とコードを見比べるといいでしょう。

swift

1import UIKit 2import Charts 3 4class ViewController: UIViewController { 5 6 @IBOutlet weak var pieChart: PieChartView! 7 8 //UIButtonをタップすることで下記の追記した二つの関数を呼び出しチャートを書き換えます。 9 //追記 10 @IBAction func tap(_ sender: UIButton) { 11 let hourValues = [9, 3, 5, 7.0] 12 let set = makeSet(values: hourValues) 13 change(types: set.types, values: set.values) 14 } 15 16 override func viewDidLoad() { 17 super.viewDidLoad() 18 makePieChart() 19 } 20 21 override func didReceiveMemoryWarning() { 22 super.didReceiveMemoryWarning() 23 } 24 25 26 27 //追記 だいたいこういう感じでvaluesを元にしたり他の引数を入れたりしてFormatterに入れる引数を作ったりします 28 func makeSet(values: [Double]) -> (types: [ActivityType], values: [Double]) { 29 let minutesValues = values.map{$0 * 60} // 分表記 30 let types: [ActivityType] = [.sleeping, .blank, .training, .blank] 31 return (types, minutesValues) 32 } 33 34 //追記 ここではすでに作られたpieChartの一部分だけを修正して、それをpieChartに通知します。そうすることで画面に表示されているチャートの情報が書き換わります。 35 func change(types: [ActivityType], values: [Double]) { 36 guard let dataSet = pieChart.data?.dataSets.first as? PieChartDataSet else { 37 fatalError() 38 } 39 let entries = values.map{PieChartDataEntry(value: Double($0))} 40 dataSet.values = entries 41 dataSet.valueFormatter = CustomLabelFomatter(types: types, values: values) 42 pieChart.notifyDataSetChanged() 43 } 44 45 func makePieChart() { 46 let hourValues = [8, 4, 2, 9.6, 0.4] // <=24時間、時間表記 47 let minutesValues = hourValues.map{$0 * 60} // 分表記 48 let types: [ActivityType] = [.sleeping, .blank, .training, .blank, .training] 49 50 let entries = minutesValues.map{ element -> PieChartDataEntry in 51 let value = Double(element) 52 return PieChartDataEntry(value: value) 53 } 54 //entriesは[PieChartDataEntry] 55 let dataSet = PieChartDataSet(values: entries, label: "") 56 57 dataSet.colors = [.purple, .gray, .blue, .gray, .blue] 58 dataSet.valueFormatter = CustomLabelFomatter(types: types, values: minutesValues) //<=ここでFormatterを設定 59 60 let data = PieChartData(dataSet: dataSet) 61 pieChart.data = data 62 } 63} 64 65//下記にもありますが[String]を用意せずにtype.rawValueという形でlabelを表示していきます。ここはそのための下準備 66enum ActivityType: String { 67 case sleeping = "睡眠" 68 case training = "トレーニング" 69 case blank = "空白" 70} 71 72class CustomLabelFomatter: NSObject, IValueFormatter { 73 74 let types: [ActivityType] //最初は[String]でやっていましたがTypeを作ってやる方法に興味がありそうなのでそちらでやっています 75 let values: [Double] 76 77 init(types: [ActivityType], values: [Double]) { 78 self.types = types 79 self.values = values 80 } 81 82 func convert(value: Double) -> String { 83 guard let convertedString = values 84 .enumerated() 85 .filter({$0.element == value}) 86 .map({ tuple -> String in 87 88 let index = tuple.offset 89 let type = types[index] 90 91 //例えがvalueが5.0より少ないときは何も表示しないとの事でしたが5.0だと扱いにくいので条件を変えています。具体的にはvalueの中身を分表記にして30分未満の場合は何も表示しないとしています。なので最後のPieChartDataEntryにはlabelがありません。 92 //ちなみにこんな風に処理の途中でreturnするのを早期リターンと言います。早期リターンを行うとそこより下の処理はせずとっとと次の処理に行くのでいろんな面で優しいです。 93 if value < 0.5 * 60 { 94 print("早期リターン") 95 return "" 96 //ちなみにこんな風に処理の途中でreturnするのを早期リターンと言います。早期リターンを行うとそこより下の処理はせずとっとと次の処理に行くのでいろんな面で優しいです。 97 } 98 99 print("処理継続中") 100 101 switch type { //今回はあまりメリットがありませんがswitch + enumはかなり強力です。漏れがあるとXcodeからお叱りが飛んできます。単純な処理だけど場合分けの量が多い時なんかはif文よりswitch文 + enumの方が楽でしょうね。 102 case .blank: 103 return "" //.blankの場合は何も表示しません 104 default: 105 return type.rawValue //が、そのほかのtypeだった場合enumのrawValueを呼び出します。こいつはActivityTypeを見れば分かりますがStringを返しています。 106 } 107 108 }).first else {fatalError()} 109 110 return convertedString 111 } 112 113 func stringForValue(_ value: Double, entry: ChartDataEntry, dataSetIndex: Int, viewPortHandler: ViewPortHandler?) -> String { 114 print(value) //ここでのvalueは各々のPieChartDataEntryのvalueです 115 return convert(value: value) 116 } 117} 118

投稿2018/09/19 15:12

編集2018/09/21 03:48
xAxis

総合スコア1349

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Hayato1201

2018/09/20 01:59

前回の質問も考慮に入れた回答で大変助かります! 確かにclass LabelFormatterの様な場合分けができるとなお良いのですが、このTypeとはどういう事でしょうか?例えばどういった条件を付けられるのでしょうか?class LabelFormatterの方の使い方の例を示していただけるとありがたいです!
xAxis

2018/09/20 03:12

ちょっと修正してみました。だいたいこんな感じです。
Hayato1201

2018/09/20 13:29 編集

返信ありがとうございました! 理解力が乏しくて申し訳ないのですが、LabelFormatterを呼び出すときは、以下の様に、 let dataSet = PieChartDataSet(values: entries, label: "") dataSet.valueFormatter = LabelFormatter(labels: labels, values: values, type:type) の様にするという事で良いのでしょうか?その場合最初2つの引数は表示させたいlabelの配列とentry.valueに入れた値の配列という事ですが、typeに関してはどの様にすれば良いのでしょうか?そもそもvalueFormatterに対して以上の様に用いるのであっているのでしょうか? 何度もすみません!
Hayato1201

2018/09/20 13:34 編集

例えばですが、以下の様にlabelがblankのときは何も表示せずそれ以外は普通に表示という様にしたいです。 さらにvalueの値によって例えばvalueが5.0より少ないときは何も表示しないとかにもできると理想的なのですが。。。 switch type { case .blank: return "" default: return "(labels[index])" }
xAxis

2018/09/20 13:48

> dataSet.valueFormatter = LabelFormatter(labels: labels, values: values, type:type) 呼び出し方はこれで合ってますよ。 これ実はBarChartViewに対して書いたFormatterを解答欄にコピペしてそのまま修正したのでPieChartViewに対してこのままコピペはうまく動かないかもですね(というか今見直したら動きそうもないです)。 Xcode立ち上げて書いてみるのでしばしお待ちください。
xAxis

2018/09/20 14:53

とりあえず書き込めるだけ書き込んだので色々見るなり分からなければ質問なりどうぞ。ちなみにいろんなやり方があるのであくまでもほんの一例です。
Hayato1201

2018/09/21 00:59

ここまで丁寧に書いていただき助かります。。。! また質問なのですが、自分の場合、ラベルの値は動的に変わるものなので、あらかじめtypeのcaseを以上の様に定義する事ができないのですが、その場合どうしたら良いでしょうか??
xAxis

2018/09/21 03:53 編集

それならFormatterに渡す配列をTypeで渡すのではなくStringで渡せばいいだけですよ。
xAxis

2018/09/21 03:55

書き換えるの面倒なのでFormatterのメンバのtypes: [ActivityType]をstrings: [String]とかにしちゃってください。
Hayato1201

2018/09/21 10:57

できました! ここまで大変ありがとうございました! 一旦ベストアンサーとさせて頂きますが、chartsについてもう少し見た目を改善したいことがあるのでまた別で質問を投稿するかとも思いますが,その時はまた回答頂ける様であれば助かります!!
xAxis

2018/09/21 11:02 編集

そうですね、質問はこのコメント欄でやるよりも別に分けて質問した方が検索する人にとっても有益でしょう。私の答えられる範囲でよければまたどうぞ。
Hayato1201

2018/10/12 03:48

今更で申し訳ないのですが、ここで回答いただいたコードについて再度疑問点があり、質問を投稿させていただいたので、もしまたご回答いただけたら大変助かります。。。。!
Hayato1201

2018/10/12 05:00

申し訳ないです、解決しました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問