前提・実現したいこと
Swiftでios-chartsを使って折れ線グラフが2つあるチャートを作っています。
「日」、「週」、「月」のタブに分けてデータ表示を行い、それぞれX軸は下記の通りです。
・日: {0時、 3時、 6時、 9時、 12時、 15時、 18時、 21時} ・週: {日、 月、 火、 水、 木、 金、 土} ・月: {2日, 6日, 10日, 14日, 18日, 22日, 26日, 30日}
IAxisValueFormatter
のstringForValue
で渡されるvalueをX軸名の数に合わせて
配列に保持しておいて、
現在チャートに表示中データの日時をUILabel
で表示しています。
- 例: 「日」タブを表示している場合
・保持しているデータは1週間分
・保持しているvalue配列 → [0, 1, 2, 3, 4, 5, 6, 7]
→ DBから0番目のデータの日付を取得
→ 取得した日付が0時の場合はyyyy年MM月dd日
とラベルに表示
→ 表示中データが日を跨ぐ場合は取得した日付に1日足したDateを取得してyyyy年MM月dd日〜yyyy年MM月dd日
とラベルに表示
→ チャートをスクロールするたびにstringForValue
のvalueが更新されてくるので、保持しているvalue配列も都度更新する
発生している問題
上記前提で「週」タブに表示を切り替えても同じようにvalue配列を作成して日付を表示したいのですが、
stringForValue
から取得されるvalueが、以下のようになります。
2020-03-31 17:25:58.235082+0900 stringForValue: 0 2020-03-31 17:25:58.235309+0900 stringForValue: 2 2020-03-31 17:25:58.235352+0900 stringForValue: 4 2020-03-31 17:25:58.235392+0900 stringForValue: 6 2020-03-31 17:25:58.235433+0900 stringForValue: 8 2020-03-31 17:25:58.235473+0900 stringForValue: 10 2020-03-31 17:25:58.235513+0900 stringForValue: 12 2020-03-31 17:25:58.244474+0900 stringForValue: 0 2020-03-31 17:25:58.244570+0900 stringForValue: 1 2020-03-31 17:25:58.244729+0900 stringForValue: 2 2020-03-31 17:25:58.244753+0900 stringForValue: 3 2020-03-31 17:25:58.244834+0900 stringForValue: 4 2020-03-31 17:25:58.244858+0900 stringForValue: 5 2020-03-31 17:25:58.244907+0900 stringForValue: 6 2020-03-31 17:25:58.248619+0900 stringForValue: 0 2020-03-31 17:25:58.249120+0900 stringForValue: 1 2020-03-31 17:25:58.249407+0900 stringForValue: 2 2020-03-31 17:25:58.249568+0900 stringForValue: 3 2020-03-31 17:25:58.249918+0900 stringForValue: 4 2020-03-31 17:25:58.250105+0900 stringForValue: 5 2020-03-31 17:25:58.250279+0900 stringForValue: 6
※最初に{0,2,4,6,8,10,12}
となった後、0~6が繰り返されます
####{0,2,4,6,8,10,12}
とならずに最初から0~6が繰り返されるようにしたいです。
該当のソースコード
swift
1 // MARK: -Viewクラスの処理 2 3 /// タブタップ時のチャートView切り替え処理 4 private func updateChartView() { 5 self.chartView?.removeFromSuperview() 6 self.chartView = nil 7 self.viewModel.resetFormatter() 8 self.addChartView() 9 self.setupChartView() 10 } 11 12 private func addChartView() { 13 self.chartView = LineChartView() 14 self.chartBaseView.addSubview(self.chartView!) 15 constrain(self.chartBaseView, self.chartView!) { base, child in 16 child.leading == child.superview!.leading 17 child.trailing == child.superview!.trailing 18 child.top == child.superview!.top 19 child.bottom == child.superview!.bottom 20 } 21 } 22 23 private func setupChartView() { 24 self.chartView?.delegate = self 25 // zoomに関する設定 26 // チャートの縮尺を変えて1週間分のデータしか表示されないようにする 27 self.chartView?.dragEnabled = true 28 self.chartView?.setScaleEnabled(true) 29 self.chartView?.pinchZoomEnabled = false 30 self.chartView?.dragDecelerationEnabled = true 31 self.chartView?.dragDecelerationFrictionCoef = 0.9 32 33 // 軸に関する設定 34 self.chartView?.xAxis.labelPosition = .bottom 35 self.chartView?.leftAxis.drawLabelsEnabled = true 36 self.chartView?.rightAxis.labelTextColor = .green 37 self.chartView?.highlightPerTapEnabled = false 38 self.chartView?.doubleTapToZoomEnabled = false 39 } 40 41 /// タブ切り替えによってX軸の内容の変更に合わせてチャート設定を更新 42 private func updateChartScale() { 43 let chartFormatter = self.viewModel.getChartFormatter() 44 self.chartView?.xAxis.valueFormatter = chartFormatter 45 self.chartView?.xAxis.labelCount = self.viewModel.getItemCount() 46 self.chartView?.xAxis.granularity = 1.0 47 } 48 49 private func setData() { 50 // 選択中タブに合わせてチャートのズームを設定 51 self.chartScale = self.viewModel.getChartScale() 52 self.chartView?.zoom(scaleX: self.chartScale, scaleY: 1, x: 0, y: 0) 53 54 self.viewModel.setChart() // ModelのsetWeekData()に続く 55 } 56 57 58 // MARK: -Modelクラスの処理 59 60 /// 「週」データをセット 61 private func setWeekData() { 62 var dataIndex: Int = 0 63 var dataSets = [LineChartDataSet]() 64 var tempEntries = [ChartDataEntry]() 65 var humidityEntries = [ChartDataEntry]() 66 let elapsedWeek = Int(self.getElapssedWeek()) // 記録開始から何週目かを取得 67 let names = ChartFormatter.weekNames 68 for weekIndex in 0..<elapsedWeek { 69 for index in 0..<names.count { 70 let tapple = self.inquireData(dataIndex: dataIndex, index: index, names: names) 71 if tapple.temp != 0 && tapple.humidity != 0 { 72 dataIndex += 1 73 } 74 tempEntries.append(ChartDataEntry(x: Double(index+(weekIndex*7)), 75 y: tapple.temp)) 76 humidityEntries.append(ChartDataEntry(x: Double(index+(weekIndex*7)), 77 y: tapple.humidity)) 78 } 79 } 80 dataSets.append(self.getDataSet(entries: tempEntries, 81 label: "温度", 82 color: .cyan)) 83 dataSets.append(self.getDataSet(entries: humidityEntries, 84 label: "湿度", 85 color: .green)) 86 self.chartData.value = LineChartData(dataSets: dataSets) // RxSwiftを利用しています 87 } 88 89/// IAxisValueFormatterを継承したカスタムクラス 90class ChartFormatter: NSObject, IAxisValueFormatter { 91 static let hourNames = ["0時", "3時", "6時", "9時", "12時", "15時", "18時", "21時"] 92 static let weekNames = ["日", "月", "火", "水", "木", "金", "土"] 93 static let monthNames = ["2日", "6日", "10日", "14日", "18日", "22日", "26日", "30日"] 94 95 96 var currentMode: ScopeMode = .date 97 var indexes = Variable<[Int]>([]) 98 var names: [String] = [] 99 let updateVisibleDateTrigger = PublishSubject<[Int]>() 100 101 private let bag = DisposeBag() 102 103 override init() { 104 super.init() 105 self.bind() 106 } 107 108 func getItemCount() -> Int { 109 return self.currentMode.getItemCount() 110 } 111 112 func stringForValue(_ value: Double, axis: AxisBase?) -> String { 113 NSLog("stringForValue: (value)") 114 var index = Int(value) 115 self.names = self.currentMode.getFormat() 116 117 self.makeIndexes(index: index, names: self.names) 118 119 index = index % self.names.count 120 return self.names[index] 121 } 122 123 private func bind() { 124 self.indexes.asObservable() 125 .filter{$0.count == self.names.count} 126 .bind(to: self.updateVisibleDateTrigger) 127 .disposed(by: self.bag) 128 } 129 130 private func makeIndexes(index: Int, names: [String]) { 131 if !self.indexes.value.contains(index) { 132 if self.indexes.value.count < names.count { 133 self.indexes.value.append(index) 134 } 135 else { 136 // 配列に入っていなかったらリスト更新 137 self.updateIndexs(index: index) 138 } 139 } 140 } 141 142 private func updateIndexs(index: Int) { 143 let first = 0 144 let last = self.indexes.value.count - 1 145 146 if self.indexes.value[first] > index { 147 self.indexes.value.removeLast() 148 self.indexes.value.insert(index, at: first) 149 } 150 else if self.indexes.value[last] < index { 151 self.indexes.value.removeFirst() 152 self.indexes.value.insert(index, at: last) 153 } 154 } 155 156} 157 158
試したこと
元々チャートの更新時にaddSubView
し直しておらず、
ただchartView.data = data
として更新していました。
その時のstringForValue
は以下の通り
2020-03-31 18:04:34.993529+0900 stringForValue: 0.0 2020-03-31 18:04:34.993613+0900 stringForValue: 4.0 2020-03-31 18:04:34.993655+0900 stringForValue: 8.0 2020-03-31 18:04:34.993694+0900 stringForValue: 12.0 2020-03-31 18:04:34.993733+0900 stringForValue: 16.0 2020-03-31 18:04:34.993772+0900 stringForValue: 20.0 2020-03-31 18:04:34.993813+0900 stringForValue: 24.0 2020-03-31 18:04:34.996908+0900 stringForValue: 28.0 2020-03-31 18:04:35.006511+0900 stringForValue: 0.0 2020-03-31 18:04:35.008893+0900 stringForValue: 1.0 2020-03-31 18:04:35.009099+0900 stringForValue: 2.0 2020-03-31 18:04:35.009153+0900 stringForValue: 3.0 2020-03-31 18:04:35.009253+0900 stringForValue: 4.0 2020-03-31 18:04:35.009341+0900 stringForValue: 5.0 2020-03-31 18:04:35.009409+0900 stringForValue: 6.0 2020-03-31 18:04:35.012912+0900 stringForValue: 0.0 2020-03-31 18:04:35.013172+0900 stringForValue: 1.0 2020-03-31 18:04:35.013499+0900 stringForValue: 2.0 2020-03-31 18:04:35.013734+0900 stringForValue: 3.0 2020-03-31 18:04:35.013945+0900 stringForValue: 4.0 2020-03-31 18:04:35.014048+0900 stringForValue: 5.0 2020-03-31 18:04:35.014250+0900 stringForValue: 6.0
・「日」タブスタートではなく、「週」タブスタートにすると、
最初に{0,2,4,6,8,10,12}
となった後0~6が繰り返されたため、
一旦charViewをremoveしてからaddし直す実装に変更。
・entitiesに入ってるデータ数はtemp, humidity共に14、chartView.labelCount
は7
【4月3日更新情報】
entities
に入ってるデータ数とchartView.labelCount
を14で合わせてみたら、
0, 1, 2,..., 11, 12, 13
と返ってきた後0~6が繰り返されました
補足情報(FW/ツールのバージョンなど)
開発環境
- MacBook Pro(Catalina 10.15)
- Xcode11.3
- Swift Language Version: Swift5
- Charts: 3.3.0
あなたの回答
tips
プレビュー