回答編集履歴
4
追記および修正
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
修正
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
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
in
|
124
|
-
|
125
|
-
sel
|
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
|
-
|
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
|
-
|
215
|
+
let type = types[index]
|
216
|
+
|
217
|
+
|
218
|
+
|
150
|
-
|
219
|
+
//例えがvalueが5.0より少ないときは何も表示しないとの事でしたが5.0だと扱いにくいので条件を変えています。具体的にはvalueの中身を分表記にして30分未満の場合は何も表示しないとしています。なので最後のPieChartDataEntryにはlabelがありません。
|
220
|
+
|
221
|
+
//ちなみにこんな風に処理の途中でreturnするのを早期リターンと言います。早期リターンを行うとそこより下の処理はせずとっとと次の処理に行くのでいろんな面で優しいです。
|
222
|
+
|
151
|
-
|
223
|
+
if value < 0.5 * 60 {
|
152
|
-
|
224
|
+
|
153
|
-
|
225
|
+
print("早期リターン")
|
154
|
-
|
155
|
-
|
226
|
+
|
156
|
-
|
157
|
-
case .training:
|
158
|
-
|
159
|
-
return "
|
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
追記
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
|
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
|
-
|
119
|
+
//type分けを行います。
|
94
120
|
|
95
121
|
|
96
122
|
|
97
|
-
init(
|
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
|
-
|
139
|
+
guard let convertedString = values
|
110
140
|
|
111
|
-
|
141
|
+
.enumerated()
|
112
142
|
|
113
|
-
i
|
143
|
+
.filter({$0.element == value})
|
114
144
|
|
115
|
-
|
145
|
+
.map({ tuple -> String in
|
116
146
|
|
117
|
-
|
147
|
+
let index = tuple.offset
|
118
148
|
|
119
|
-
|
149
|
+
switch type {
|
120
150
|
|
121
|
-
|
151
|
+
case .sleeping:
|
122
152
|
|
123
|
-
|
153
|
+
let additionalLabel = "foo"
|
124
154
|
|
125
|
-
|
155
|
+
return "(additionalLabel)(labels[index])"
|
126
156
|
|
127
|
-
|
157
|
+
case .training:
|
128
158
|
|
129
|
-
|
159
|
+
return "(labels[index])"
|
130
160
|
|
131
|
-
|
161
|
+
case .blank:
|
132
162
|
|
133
|
-
|
163
|
+
return ""
|
134
164
|
|
135
|
-
|
165
|
+
}
|
136
166
|
|
137
|
-
|
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
編集
test
CHANGED
@@ -78,9 +78,7 @@
|
|
78
78
|
|
79
79
|
また、このやり方を応用すれば任意のEntryだけ好きな文字列を表示させたりも出来ます。
|
80
80
|
|
81
|
-
|
82
|
-
|
83
|
-
ご参考までにこんな感じです。
|
81
|
+
以下のコードは実際に書いたコードをアレンジしてますが、ご参考までにこんな感じです。
|
84
82
|
|
85
83
|
|
86
84
|
|