回答編集履歴

4

追記および修正

2018/09/21 03:48

投稿

xAxis
xAxis

スコア1349

test CHANGED
@@ -104,7 +104,23 @@
104
104
 
105
105
  @IBOutlet weak var pieChart: PieChartView!
106
106
 
107
+
108
+
107
-
109
+ //UIButtonをタップすることで下記の追記した二つの関数を呼び出しチャートを書き換えます。
110
+
111
+ //追記
112
+
113
+ @IBAction func tap(_ sender: UIButton) {
114
+
115
+ let hourValues = [9, 3, 5, 7.0]
116
+
117
+ let set = makeSet(values: hourValues)
118
+
119
+ change(types: set.types, values: set.values)
120
+
121
+ }
122
+
123
+
108
124
 
109
125
  override func viewDidLoad() {
110
126
 
@@ -122,6 +138,46 @@
122
138
 
123
139
  }
124
140
 
141
+
142
+
143
+
144
+
145
+
146
+
147
+ //追記 だいたいこういう感じでvaluesを元にしたり他の引数を入れたりしてFormatterに入れる引数を作ったりします
148
+
149
+ func makeSet(values: [Double]) -> (types: [ActivityType], values: [Double]) {
150
+
151
+ let minutesValues = values.map{$0 * 60} // 分表記
152
+
153
+ let types: [ActivityType] = [.sleeping, .blank, .training, .blank]
154
+
155
+ return (types, minutesValues)
156
+
157
+ }
158
+
159
+
160
+
161
+ //追記 ここではすでに作られたpieChartの一部分だけを修正して、それをpieChartに通知します。そうすることで画面に表示されているチャートの情報が書き換わります。
162
+
163
+ func change(types: [ActivityType], values: [Double]) {
164
+
165
+ guard let dataSet = pieChart.data?.dataSets.first as? PieChartDataSet else {
166
+
167
+ fatalError()
168
+
169
+ }
170
+
171
+ let entries = values.map{PieChartDataEntry(value: Double($0))}
172
+
173
+ dataSet.values = entries
174
+
175
+ dataSet.valueFormatter = CustomLabelFomatter(types: types, values: values)
176
+
177
+ pieChart.notifyDataSetChanged()
178
+
179
+ }
180
+
125
181
 
126
182
 
127
183
  func makePieChart() {

3

修正

2018/09/21 03:48

投稿

xAxis
xAxis

スコア1349

test CHANGED
@@ -78,61 +78,125 @@
78
78
 
79
79
  また、このやり方を応用すれば任意のEntryだけ好きな文字列を表示させたりも出来ます。
80
80
 
81
+
82
+
83
+ (追記および修正)
84
+
85
+
86
+
81
- 強引な書き方をしたので以下のコードはご参考までに
87
+ ご要望にお応えてみました。
88
+
89
+ 丸々コピペしても動きます(動作確認済み)。Outlet接続はちゃんと再設定してください。あとコンソールに色々printした結果が出てくるのでその結果とコードを見比べるといいでしょう。
82
90
 
83
91
 
84
92
 
85
93
  ```swift
86
94
 
87
- //entryの種類によってタイプ分けを行います。
88
-
89
- enum ValueType {
90
-
91
- case sleeping
92
-
93
- case training
94
-
95
- case blank
96
-
97
- }
98
-
99
-
100
-
101
- class LabelFormatter: NSObject, IValueFormatter {
102
-
103
-
104
-
105
- let labels: [String] //表示させたいlabelの配列です。例えば"7時間30分"とか、"トレーニング"
106
-
107
- //とかStringで入れておきます。
108
-
109
-
110
-
111
- let values: [Double] //entry.valueに入れた値の配列です。labels[n]とvalues[n]は
112
-
113
- //相関関係にある状態にします。
114
-
115
-
116
-
117
- let type: ValueType //typeは上記のValueTypeです。typeによって処理を変えたい時に
118
-
119
- //type分けを行います。
120
-
121
-
122
-
123
- init(labels: [String], values: [Double], type: ValueType) {
124
-
125
- self.labels = labels
95
+ import UIKit
96
+
97
+ import Charts
98
+
99
+
100
+
101
+ class ViewController: UIViewController {
102
+
103
+
104
+
105
+ @IBOutlet weak var pieChart: PieChartView!
106
+
107
+
108
+
109
+ override func viewDidLoad() {
110
+
111
+ super.viewDidLoad()
112
+
113
+ makePieChart()
114
+
115
+ }
116
+
117
+
118
+
119
+ override func didReceiveMemoryWarning() {
120
+
121
+ super.didReceiveMemoryWarning()
122
+
123
+ }
124
+
125
+
126
+
127
+ func makePieChart() {
128
+
129
+ let hourValues = [8, 4, 2, 9.6, 0.4] // <=24時間、時間表記
130
+
131
+ let minutesValues = hourValues.map{$0 * 60} // 分表記
132
+
133
+ let types: [ActivityType] = [.sleeping, .blank, .training, .blank, .training]
134
+
135
+
136
+
137
+ let entries = minutesValues.map{ element -> PieChartDataEntry in
138
+
139
+ let value = Double(element)
140
+
141
+ return PieChartDataEntry(value: value)
142
+
143
+ }
144
+
145
+ //entriesは[PieChartDataEntry]
146
+
147
+ let dataSet = PieChartDataSet(values: entries, label: "")
148
+
149
+
150
+
151
+ dataSet.colors = [.purple, .gray, .blue, .gray, .blue]
152
+
153
+ dataSet.valueFormatter = CustomLabelFomatter(types: types, values: minutesValues) //<=ここでFormatterを設定
154
+
155
+
156
+
157
+ let data = PieChartData(dataSet: dataSet)
158
+
159
+ pieChart.data = data
160
+
161
+ }
162
+
163
+ }
164
+
165
+
166
+
167
+ //下記にもありますが[String]を用意せずにtype.rawValueという形でlabelを表示していきます。ここはそのための下準備
168
+
169
+ enum ActivityType: String {
170
+
171
+ case sleeping = "睡眠"
172
+
173
+ case training = "トレーニング"
174
+
175
+ case blank = "空白"
176
+
177
+ }
178
+
179
+
180
+
181
+ class CustomLabelFomatter: NSObject, IValueFormatter {
182
+
183
+
184
+
185
+ let types: [ActivityType] //最初は[String]でやっていましたがTypeを作ってやる方法に興味がありそうなのでそちらでやっています
186
+
187
+ let values: [Double]
188
+
189
+
190
+
191
+ init(types: [ActivityType], values: [Double]) {
192
+
193
+ self.types = types
126
194
 
127
195
  self.values = values
128
196
 
129
- self.type = type
130
-
131
- }
197
+ }
132
-
133
-
134
-
135
- //typeが.sleepingの時は文字列を追加、typeが.trainingの時はそのまま、typeがblankの時は表示無しになっています。
198
+
199
+
136
200
 
137
201
  func convert(value: Double) -> String {
138
202
 
@@ -144,29 +208,49 @@
144
208
 
145
209
  .map({ tuple -> String in
146
210
 
211
+
212
+
147
213
  let index = tuple.offset
148
214
 
149
- switch type {
215
+ let type = types[index]
216
+
217
+
218
+
150
-
219
+ //例えがvalueが5.0より少ないときは何も表示しないとの事でしたが5.0だと扱いにくいので条件を変えています。具体的にはvalueの中身を分表記にして30分未満の場合は何も表示しないとしています。なので最後のPieChartDataEntryにはlabelがありません。
220
+
221
+ //ちなみにこんな風に処理の途中でreturnするのを早期リターンと言います。早期リターンを行うとそこより下の処理はせずとっとと次の処理に行くのでいろんな面で優しいです。
222
+
151
- case .sleeping:
223
+ if value < 0.5 * 60 {
152
-
224
+
153
- let additionalLabel = "foo"
225
+ print("早期リターン")
154
-
155
- return "(additionalLabel)(labels[index])"
226
+
156
-
157
- case .training:
158
-
159
- return "(labels[index])"
227
+ return ""
228
+
229
+ //ちなみにこんな風に処理の途中でreturnするのを早期リターンと言います。早期リターンを行うとそこより下の処理はせずとっとと次の処理に行くのでいろんな面で優しいです。
230
+
231
+ }
232
+
233
+
234
+
235
+ print("処理継続中")
236
+
237
+
238
+
239
+ switch type { //今回はあまりメリットがありませんがswitch + enumはかなり強力です。漏れがあるとXcodeからお叱りが飛んできます。単純な処理だけど場合分けの量が多い時なんかはif文よりswitch文 + enumの方が楽でしょうね。
160
240
 
161
241
  case .blank:
162
242
 
163
- return ""
243
+ return "" //.blankの場合は何も表示しません
244
+
245
+ default:
246
+
247
+ return type.rawValue //が、そのほかのtypeだった場合enumのrawValueを呼び出します。こいつはActivityTypeを見れば分かりますがStringを返しています。
164
248
 
165
249
  }
166
250
 
167
- })
251
+
168
-
252
+
169
- .first else {fatalError()}
253
+ }).first else {fatalError()}
170
254
 
171
255
 
172
256
 
@@ -178,12 +262,14 @@
178
262
 
179
263
  func stringForValue(_ value: Double, entry: ChartDataEntry, dataSetIndex: Int, viewPortHandler: ViewPortHandler?) -> String {
180
264
 
265
+ print(value) //ここでのvalueは各々のPieChartDataEntryのvalueです
266
+
181
267
  return convert(value: value)
182
268
 
183
269
  }
184
270
 
185
-
186
-
187
- }
271
+ }
272
+
273
+
188
274
 
189
275
  ```

2

追記

2018/09/20 14:50

投稿

xAxis
xAxis

スコア1349

test CHANGED
@@ -78,23 +78,51 @@
78
78
 
79
79
  また、このやり方を応用すれば任意のEntryだけ好きな文字列を表示させたりも出来ます。
80
80
 
81
- 以下のコードは実際に書いたコードをアレンジしてますが、ご参考までにこんな感じです
81
+ 強引な書き方をしたので以下のコードはご参考までに。
82
82
 
83
83
 
84
84
 
85
85
  ```swift
86
86
 
87
+ //entryの種類によってタイプ分けを行います。
88
+
89
+ enum ValueType {
90
+
91
+ case sleeping
92
+
93
+ case training
94
+
95
+ case blank
96
+
97
+ }
98
+
99
+
100
+
87
101
  class LabelFormatter: NSObject, IValueFormatter {
88
102
 
89
103
 
90
104
 
91
- let values: [Double]
105
+ let labels: [String] //表示させたいlabelの配列です。例えば"7時間30分"とか、"トレーニング"
92
106
 
107
+ //とかStringで入れておきます。
108
+
109
+
110
+
111
+ let values: [Double] //entry.valueに入れた値の配列です。labels[n]とvalues[n]は
112
+
113
+ //相関関係にある状態にします。
114
+
115
+
116
+
117
+ let type: ValueType //typeは上記のValueTypeです。typeによって処理を変えたい時に
118
+
93
- let type: Type
119
+ //type分けを行います。
94
120
 
95
121
 
96
122
 
97
- init(_ values: [Double], type: Type) {
123
+ init(labels: [String], values: [Double], type: ValueType) {
124
+
125
+ self.labels = labels
98
126
 
99
127
  self.values = values
100
128
 
@@ -104,39 +132,45 @@
104
132
 
105
133
 
106
134
 
135
+ //typeが.sleepingの時は文字列を追加、typeが.trainingの時はそのまま、typeがblankの時は表示無しになっています。
136
+
107
137
  func convert(value: Double) -> String {
108
138
 
109
- switch type {
139
+ guard let convertedString = values
110
140
 
111
- case .A:
141
+ .enumerated()
112
142
 
113
- if value == 0 {
143
+ .filter({$0.element == value})
114
144
 
115
- return ""
145
+ .map({ tuple -> String in
116
146
 
117
- } else {
147
+ let index = tuple.offset
118
148
 
119
- return "(Int(value))"
149
+ switch type {
120
150
 
121
- }
151
+ case .sleeping:
122
152
 
123
- case .B:
153
+ let additionalLabel = "foo"
124
154
 
125
- if value < 1 {
155
+ return "(additionalLabel)(labels[index])"
126
156
 
127
- return ""
157
+ case .training:
128
158
 
129
- } else {
159
+ return "(labels[index])"
130
160
 
131
- return "(Int(value))"
161
+ case .blank:
132
162
 
133
- }
163
+ return ""
134
164
 
135
- case .C, .D, .E:
165
+ }
136
166
 
137
- return "((Int(value)))"
167
+ })
138
168
 
139
- }
169
+ .first else {fatalError()}
170
+
171
+
172
+
173
+ return convertedString
140
174
 
141
175
  }
142
176
 
@@ -153,7 +187,3 @@
153
187
  }
154
188
 
155
189
  ```
156
-
157
-
158
-
159
- これは任意のTypeの特定の条件下では文字列を表示させず、それ以外の場合は整数表記の文字列を表示する、といった具合のものになります。

1

編集

2018/09/20 03:12

投稿

xAxis
xAxis

スコア1349

test CHANGED
@@ -78,9 +78,7 @@
78
78
 
79
79
  また、このやり方を応用すれば任意のEntryだけ好きな文字列を表示させたりも出来ます。
80
80
 
81
-
82
-
83
- ご参考までにこんな感じです。
81
+ 以下のコードは実際に書いたコードをアレンジしてますが、ご参考までにこんな感じです。
84
82
 
85
83
 
86
84