teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

4

追記および修正

2018/09/21 03:48

投稿

xAxis
xAxis

スコア1349

answer CHANGED
@@ -51,7 +51,15 @@
51
51
  class ViewController: UIViewController {
52
52
 
53
53
  @IBOutlet weak var pieChart: PieChartView!
54
+
54
-
55
+ //UIButtonをタップすることで下記の追記した二つの関数を呼び出しチャートを書き換えます。
56
+ //追記
57
+ @IBAction func tap(_ sender: UIButton) {
58
+ let hourValues = [9, 3, 5, 7.0]
59
+ let set = makeSet(values: hourValues)
60
+ change(types: set.types, values: set.values)
61
+ }
62
+
55
63
  override func viewDidLoad() {
56
64
  super.viewDidLoad()
57
65
  makePieChart()
@@ -60,6 +68,26 @@
60
68
  override func didReceiveMemoryWarning() {
61
69
  super.didReceiveMemoryWarning()
62
70
  }
71
+
72
+
73
+
74
+ //追記 だいたいこういう感じでvaluesを元にしたり他の引数を入れたりしてFormatterに入れる引数を作ったりします
75
+ func makeSet(values: [Double]) -> (types: [ActivityType], values: [Double]) {
76
+ let minutesValues = values.map{$0 * 60} // 分表記
77
+ let types: [ActivityType] = [.sleeping, .blank, .training, .blank]
78
+ return (types, minutesValues)
79
+ }
80
+
81
+ //追記 ここではすでに作られたpieChartの一部分だけを修正して、それをpieChartに通知します。そうすることで画面に表示されているチャートの情報が書き換わります。
82
+ func change(types: [ActivityType], values: [Double]) {
83
+ guard let dataSet = pieChart.data?.dataSets.first as? PieChartDataSet else {
84
+ fatalError()
85
+ }
86
+ let entries = values.map{PieChartDataEntry(value: Double($0))}
87
+ dataSet.values = entries
88
+ dataSet.valueFormatter = CustomLabelFomatter(types: types, values: values)
89
+ pieChart.notifyDataSetChanged()
90
+ }
63
91
 
64
92
  func makePieChart() {
65
93
  let hourValues = [8, 4, 2, 9.6, 0.4] // <=24時間、時間表記

3

修正

2018/09/21 03:48

投稿

xAxis
xAxis

スコア1349

answer CHANGED
@@ -38,58 +38,101 @@
38
38
  ```
39
39
 
40
40
  また、このやり方を応用すれば任意のEntryだけ好きな文字列を表示させたりも出来ます。
41
- 強引な書き方をしたので以下のコードはご参考までに。
42
41
 
42
+ (追記および修正)
43
+
44
+ ご要望にお応えしてみました。
45
+ 丸々コピペしても動きます(動作確認済み)。Outlet接続はちゃんと再設定してください。あとコンソールに色々printした結果が出てくるのでその結果とコードを見比べるといいでしょう。
46
+
43
47
  ```swift
44
- //entryの種類によってタイプ分けを行います。
45
- enum ValueType {
46
- case sleeping
47
- case training
48
+ import UIKit
48
- case blank
49
+ import Charts
49
- }
50
50
 
51
- class LabelFormatter: NSObject, IValueFormatter {
51
+ class ViewController: UIViewController {
52
+
53
+ @IBOutlet weak var pieChart: PieChartView!
52
54
 
53
- let labels: [String] //表示させたいlabelの配列です。例えば"7時間30分"とか、"トレーニング"
54
- //とかStringで入れておきます。
55
+ override func viewDidLoad() {
56
+ super.viewDidLoad()
57
+ makePieChart()
58
+ }
55
59
 
60
+ override func didReceiveMemoryWarning() {
61
+ super.didReceiveMemoryWarning()
62
+ }
63
+
64
+ func makePieChart() {
65
+ let hourValues = [8, 4, 2, 9.6, 0.4] // <=24時間、時間表記
66
+ let minutesValues = hourValues.map{$0 * 60} // 分表記
67
+ let types: [ActivityType] = [.sleeping, .blank, .training, .blank, .training]
68
+
69
+ let entries = minutesValues.map{ element -> PieChartDataEntry in
70
+ let value = Double(element)
71
+ return PieChartDataEntry(value: value)
72
+ }
73
+ //entriesは[PieChartDataEntry]
56
- let values: [Double] //entry.valueに入れた値の配列です。labels[n]とvalues[n]は
74
+ let dataSet = PieChartDataSet(values: entries, label: "")
75
+
76
+ dataSet.colors = [.purple, .gray, .blue, .gray, .blue]
77
+ dataSet.valueFormatter = CustomLabelFomatter(types: types, values: minutesValues) //<=ここでFormatterを設定
78
+
79
+ let data = PieChartData(dataSet: dataSet)
57
- //相関関係にある状態にします。
80
+ pieChart.data = data
81
+ }
82
+ }
58
83
 
59
- let type: ValueType //typeは上のValueTypeで。typeよって処理変えた時に
84
+ //にもありまが[String]を用意せずtype.rawValueという形でlabel表示してきます。ここはそのための下準備
60
- //type分けを行います。
85
+ enum ActivityType: String {
86
+ case sleeping = "睡眠"
87
+ case training = "トレーニング"
88
+ case blank = "空白"
89
+ }
90
+
91
+ class CustomLabelFomatter: NSObject, IValueFormatter {
61
92
 
93
+ let types: [ActivityType] //最初は[String]でやっていましたがTypeを作ってやる方法に興味がありそうなのでそちらでやっています
94
+ let values: [Double]
95
+
62
- init(labels: [String], values: [Double], type: ValueType) {
96
+ init(types: [ActivityType], values: [Double]) {
63
- self.labels = labels
97
+ self.types = types
64
98
  self.values = values
65
- self.type = type
66
99
  }
67
100
 
68
- //typeが.sleepingの時は文字列を追加、typeが.trainingの時はそのまま、typeがblankの時は表示無しになっています。
69
101
  func convert(value: Double) -> String {
70
102
  guard let convertedString = values
71
103
  .enumerated()
72
104
  .filter({$0.element == value})
73
105
  .map({ tuple -> String in
106
+
74
107
  let index = tuple.offset
75
- switch type {
108
+ let type = types[index]
109
+
110
+ //例えがvalueが5.0より少ないときは何も表示しないとの事でしたが5.0だと扱いにくいので条件を変えています。具体的にはvalueの中身を分表記にして30分未満の場合は何も表示しないとしています。なので最後のPieChartDataEntryにはlabelがありません。
111
+ //ちなみにこんな風に処理の途中でreturnするのを早期リターンと言います。早期リターンを行うとそこより下の処理はせずとっとと次の処理に行くのでいろんな面で優しいです。
76
- case .sleeping:
112
+ if value < 0.5 * 60 {
77
- let additionalLabel = "foo"
113
+ print("早期リターン")
78
- return "(additionalLabel)(labels[index])"
79
- case .training:
80
- return "(labels[index])"
114
+ return ""
115
+ //ちなみにこんな風に処理の途中でreturnするのを早期リターンと言います。早期リターンを行うとそこより下の処理はせずとっとと次の処理に行くのでいろんな面で優しいです。
116
+ }
117
+
118
+ print("処理継続中")
119
+
120
+ switch type { //今回はあまりメリットがありませんがswitch + enumはかなり強力です。漏れがあるとXcodeからお叱りが飛んできます。単純な処理だけど場合分けの量が多い時なんかはif文よりswitch文 + enumの方が楽でしょうね。
81
121
  case .blank:
82
- return ""
122
+ return "" //.blankの場合は何も表示しません
123
+ default:
124
+ return type.rawValue //が、そのほかのtypeだった場合enumのrawValueを呼び出します。こいつはActivityTypeを見れば分かりますがStringを返しています。
83
125
  }
84
- })
126
+
85
- .first else {fatalError()}
127
+ }).first else {fatalError()}
86
128
 
87
129
  return convertedString
88
130
  }
89
131
 
90
132
  func stringForValue(_ value: Double, entry: ChartDataEntry, dataSetIndex: Int, viewPortHandler: ViewPortHandler?) -> String {
133
+ print(value) //ここでのvalueは各々のPieChartDataEntryのvalueです
91
134
  return convert(value: value)
92
135
  }
93
-
94
136
  }
137
+
95
138
  ```

2

追記

2018/09/20 14:50

投稿

xAxis
xAxis

スコア1349

answer CHANGED
@@ -38,36 +38,53 @@
38
38
  ```
39
39
 
40
40
  また、このやり方を応用すれば任意のEntryだけ好きな文字列を表示させたりも出来ます。
41
- 以下のコードは実際に書いたコードをアレンジしてますが、ご参考までにこんな感じです
41
+ 強引な書き方をしたので以下のコードはご参考までに。
42
42
 
43
43
  ```swift
44
+ //entryの種類によってタイプ分けを行います。
45
+ enum ValueType {
46
+ case sleeping
47
+ case training
48
+ case blank
49
+ }
50
+
44
51
  class LabelFormatter: NSObject, IValueFormatter {
45
52
 
53
+ let labels: [String] //表示させたいlabelの配列です。例えば"7時間30分"とか、"トレーニング"
54
+ //とかStringで入れておきます。
55
+
56
+ let values: [Double] //entry.valueに入れた値の配列です。labels[n]とvalues[n]は
46
- let values: [Double]
57
+ //相関関係にある状態にします。
58
+
59
+ let type: ValueType //typeは上記のValueTypeです。typeによって処理を変えたい時に
47
- let type: Type
60
+ //type分けを行います。
48
61
 
49
- init(_ values: [Double], type: Type) {
62
+ init(labels: [String], values: [Double], type: ValueType) {
63
+ self.labels = labels
50
64
  self.values = values
51
65
  self.type = type
52
66
  }
53
67
 
68
+ //typeが.sleepingの時は文字列を追加、typeが.trainingの時はそのまま、typeがblankの時は表示無しになっています。
54
69
  func convert(value: Double) -> String {
70
+ guard let convertedString = values
71
+ .enumerated()
72
+ .filter({$0.element == value})
73
+ .map({ tuple -> String in
74
+ let index = tuple.offset
55
- switch type {
75
+ switch type {
76
+ case .sleeping:
77
+ let additionalLabel = "foo"
78
+ return "(additionalLabel)(labels[index])"
79
+ case .training:
80
+ return "(labels[index])"
56
- case .A:
81
+ case .blank:
57
- if value == 0 {
58
- return ""
82
+ return ""
59
- } else {
60
- return "(Int(value))"
61
- }
83
+ }
62
- case .B:
63
- if value < 1 {
64
- return ""
65
- } else {
66
- return "(Int(value))"
67
- }
84
+ })
68
- case .C, .D, .E:
85
+ .first else {fatalError()}
86
+
69
- return "((Int(value)))"
87
+ return convertedString
70
- }
71
88
  }
72
89
 
73
90
  func stringForValue(_ value: Double, entry: ChartDataEntry, dataSetIndex: Int, viewPortHandler: ViewPortHandler?) -> String {
@@ -75,6 +92,4 @@
75
92
  }
76
93
 
77
94
  }
78
- ```
95
+ ```
79
-
80
- これは任意のTypeの特定の条件下では文字列を表示させず、それ以外の場合は整数表記の文字列を表示する、といった具合のものになります。

1

編集

2018/09/20 03:12

投稿

xAxis
xAxis

スコア1349

answer CHANGED
@@ -38,9 +38,8 @@
38
38
  ```
39
39
 
40
40
  また、このやり方を応用すれば任意のEntryだけ好きな文字列を表示させたりも出来ます。
41
+ 以下のコードは実際に書いたコードをアレンジしてますが、ご参考までにこんな感じです。
41
42
 
42
- ご参考までにこんな感じです。
43
-
44
43
  ```swift
45
44
  class LabelFormatter: NSObject, IValueFormatter {
46
45