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

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

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

RealmとはSQLiteやCore Dataに代わるモバイルデータベースです。iOSとAndroidの両方でサポートされています。

Swift

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

Q&A

解決済

1回答

1132閲覧

【Swift-Realm・Charts】グラフのX軸が1日ずつずれた日付になるようにしたい

miyuki

総合スコア23

Realm

RealmとはSQLiteやCore Dataに代わるモバイルデータベースです。iOSとAndroidの両方でサポートされています。

Swift

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

0グッド

0クリップ

投稿2023/04/04 08:46

実現したいこと

Chartsを利用して、Realmに保存されたデータを元にグラフを描画するアプリを作っています。
X軸には日付、Y軸には値を入れたいと考えています。(X軸の日付に対応する値がない場合もあります)
1週間ごとのデータをボタンで切り替えてみれるようにしています。

問題

今、Realmに複数データが保存されている場合のグラフは正しくX軸に日付が入っているのですが、
イメージ説明

このように1週間のデータが少ない場合はX軸の日付が正しく表示されません。
イメージ説明

ChartDataEntryでXの値に、データがある日の日付のみ入れていることが問題なのだと思うのですが、ChartDataEntryではX、Yそれぞれの値を個々に組み込むことができないため(Missing argument for parameter 'x' in callというようにエラーが出てしまいます)、どのようにすればX軸に1日ずつずらした正しい日付を表示できるのかわからず苦戦しています。

該当のソースコード

Swift

1class GraphViewController: UIViewController { 2 3 let realm = try! Realm() 4 var post: Post! 5 var points = try! Realm().objects(Post.self).sorted(byKeyPath: "date", ascending: true) 6 7 var page = 0 8 var maxPage = 0 9 10 weak var axisFormatDelegate: AxisValueFormatter? 11 var dateInterval = 0 12 13 @IBOutlet weak var lineChartView: LineChartView! 14 @IBOutlet weak var laterButton: UIButton! 15 @IBOutlet weak var formerButton: UIButton! 16 17 override func viewDidLoad() { 18 super.viewDidLoad() 19 20 axisFormatDelegate = self 21 createChartView(page: 0) 22 23 var firstPost = points.first?.date 24 var lastPost = points.last?.date 25 //経過日数 26 dateInterval = Int(lastPost!.timeIntervalSince(firstPost!)) / (24 * 60 * 60) 27 maxPage = dateInterval / 7 - 1 28 29 if page == maxPage { 30 formerButton.isHidden = true 31 } 32 33 if page == 0{ 34 laterButton.isHidden = true 35 } 36 37 } 38 @IBAction func LaterButton(_ sender: Any) { 39 formerButton.isHidden = false 40 page -= 1 41 if page == 0{ 42 laterButton.isHidden = true 43 createChartView(page: page) 44 } else { 45 laterButton.isHidden = false 46 createChartView(page: page) 47 } 48 } 49 @IBAction func FormerButton(_ sender: Any) { 50 laterButton.isHidden = false 51 page += 1 52 if page == maxPage + 1 { 53 formerButton.isHidden = true 54 createChartView(page: page) 55 } else { 56 formerButton.isHidden = false 57 createChartView(page: page) 58 } 59 } 60 func createChartView(page: Int){ 61 //Y軸の最大値・最小値 62 lineChartView.leftAxis.axisMaximum = 100 63 lineChartView.leftAxis.axisMinimum = -50 64 //Xラベルと値がずれないように 65 lineChartView.xAxis.avoidFirstLastClippingEnabled = false 66 67 lineChartView.xAxis.labelCount = 7 68 //X軸ラベルを強制的に有効化 69 lineChartView.xAxis.forceLabelsEnabled = true 70 lineChartView.xAxis.granularityEnabled = true 71 lineChartView.xAxis.granularity = 1 72 73 //データセット 74 var lineDateEntry = [ChartDataEntry]() 75 //realmからとってきた全データ 76 let yAxis = getPercentFromDatabase(page: page) 77 var yAxisArray = [Double]() 78 var xAxisDateArray = [Date]() 79 for i in 0..<yAxis.count { 80 yAxisArray.append(Double(yAxis[i].percent)!) 81 let xAxisDate: TimeInterval = yAxis[i].date.timeIntervalSince1970 82 let dataEntry = ChartDataEntry(x: Double(xAxisDate), y: Double(yAxis[i].percent)!) 83 lineDateEntry.append(dataEntry) 84 } 85 let chartDataSet = LineChartDataSet(lineDateEntry) 86 let chartData = LineChartData(dataSet: chartDataSet) 87 lineChartView.data = chartData 88 let xAxis = lineChartView.xAxis 89 xAxis.valueFormatter = axisFormatDelegate 90 } 91 92 func getPercentFromDatabase(page: Int) -> Results<Post>{ 93 do { 94 let realm = try Realm() 95 //1番最近のもの 96 var lastDay = realm.objects(Post.self).last?.date 97 //1週間の中で1番古い 98 var sixDaysAgo = Calendar.current.date(byAdding: .day, value: -6 + -7 * page, to: lastDay!) 99 //1週間の中で1番最近 100 var latestDay = Calendar.current.date(byAdding: .day, value: -7 * page, to: lastDay!) 101 var predicate = NSPredicate(format: "date >= %@ && date <= %@", sixDaysAgo! as CVarArg, latestDay! as CVarArg) 102 return realm.objects(Post.self).filter(predicate) 103 } catch let error as NSError { 104 fatalError(error.localizedDescription) 105 } 106 } 107 108} 109 110//X軸の値を管理 111extension GraphViewController: AxisValueFormatter { 112 113 func stringForValue(_ value: Double, axis: AxisBase?) -> String { 114 let dateFormatter = DateFormatter() 115 dateFormatter.dateFormat = "MM/dd" 116 return dateFormatter.string(from: Date(timeIntervalSince1970: value)) 117 } 118}

試したこと

こちらを参考にして、AxisValueFormatterを管理しているExtensionの中で日付を定義しようとしたのですが、

Swift

1let realm = try Realm()

と記述すると

Call can throw, but errors cannot be thrown out of a property initializer

とエラーが出てしまい上手く使いこなせませんでした。
https://stackoverflow.com/questions/51333345/ios-charts-library-x-axis-labels-without-backing-data-not-showing

拙い説明ですが、ご教授いただけますと大変ありがたいです。

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2023/04/04 09:02

データが1つだけなのですかね。 このデータの場合、どのように表示されて欲しいのでしょうか? 3/27のデータが存在するみたいですので、 そのデータが含まれる1週間というと、 3/26~4/1がx軸に表示されて欲しいということになるでしょうか? x軸の1つ分の間隔と、 x軸の最小の値と、 x軸の最大の値と、 これらはパラメータ的なもので指定できそうに思いましたので、 この辺りをドキュメントから調べてみると良いのかなと思いました。
guest

回答1

0

ベストアンサー

コメントしてみたのですが、
その内容で実現できそうでしょうか?
3/26~4/1の1週間を表示するサンプルで修正してみました。
いかがでしょうか?
(// ---のようなコメントが主な修正ポイントです)
(手元にRealmがないので、テストデータのコードを直接記述しています)

swift

1import UIKit 2import Charts 3 4class GraphViewController: UIViewController { 5 6// let realm = try! Realm() 7// var post: Post! 8// var points = try! Realm().objects(Post.self).sorted(byKeyPath: "date", ascending: true) 9 10 var page = 0 11 var maxPage = 0 12 13 weak var axisFormatDelegate: AxisValueFormatter? 14 var dateInterval = 0 15 16 @IBOutlet weak var lineChartView: LineChartView! 17 @IBOutlet weak var laterButton: UIButton! 18 @IBOutlet weak var formerButton: UIButton! 19 20 override func viewDidLoad() { 21 super.viewDidLoad() 22 23 axisFormatDelegate = self 24 createChartView(page: 0) 25 26// var firstPost = points.first?.date 27// var lastPost = points.last?.date 28 let firstPost: Date? = Date() 29 let lastPost: Date? = Date() 30 //経過日数 31 dateInterval = Int(lastPost!.timeIntervalSince(firstPost!)) / (24 * 60 * 60) 32 maxPage = dateInterval / 7 - 1 33 34 if page == maxPage { 35 formerButton.isHidden = true 36 } 37 38 if page == 0{ 39 laterButton.isHidden = true 40 } 41 42 } 43 @IBAction func LaterButton(_ sender: Any) { 44 formerButton.isHidden = false 45 page -= 1 46 if page == 0{ 47 laterButton.isHidden = true 48 createChartView(page: page) 49 } else { 50 laterButton.isHidden = false 51 createChartView(page: page) 52 } 53 } 54 @IBAction func FormerButton(_ sender: Any) { 55 laterButton.isHidden = false 56 page += 1 57 if page == maxPage + 1 { 58 formerButton.isHidden = true 59 createChartView(page: page) 60 } else { 61 formerButton.isHidden = false 62 createChartView(page: page) 63 } 64 } 65 func createChartView(page: Int){ 66 //Y軸の最大値・最小値 67 lineChartView.leftAxis.axisMaximum = 100 68 lineChartView.leftAxis.axisMinimum = -50 69 //Xラベルと値がずれないように 70 lineChartView.xAxis.avoidFirstLastClippingEnabled = false 71 72 lineChartView.xAxis.labelCount = 7 73 //X軸ラベルを強制的に有効化 74 lineChartView.xAxis.forceLabelsEnabled = true 75 lineChartView.xAxis.granularityEnabled = true 76// lineChartView.xAxis.granularity = 1 77 78 // --- x軸の最小の間隔は1日分にします。 79 lineChartView.xAxis.granularity = 1 * 24 * 60 * 60 80 // --- x軸の最小の値は週の初め(3/26)で、最大の値は週の終わり(4/1)にします。 81 lineChartView.xAxis.axisMinimum = dateFormatter.date(from: "2023/03/26")!.timeIntervalSince1970 82 lineChartView.xAxis.axisMaximum = dateFormatter.date(from: "2023/04/01")!.timeIntervalSince1970 83 84 //データセット 85 var lineDateEntry = [ChartDataEntry]() 86 //realmからとってきた全データ 87// let yAxis = getPercentFromDatabase(page: page) 88// var yAxisArray = [Double]() 89// var xAxisDateArray = [Date]() 90// for i in 0..<yAxis.count { 91// yAxisArray.append(Double(yAxis[i].percent)!) 92// let xAxisDate: TimeInterval = yAxis[i].date.timeIntervalSince1970 93// let dataEntry = ChartDataEntry(x: Double(xAxisDate), y: Double(yAxis[i].percent)!) 94// lineDateEntry.append(dataEntry) 95// } 96 97 // --- テストのためのデータです。 98 lineDateEntry.append(ChartDataEntry( 99 x: dateFormatter.date(from: "2023/03/27")!.timeIntervalSince1970, 100 y: -15.0)) 101 102 let chartDataSet = LineChartDataSet(lineDateEntry) 103 let chartData = LineChartData(dataSet: chartDataSet) 104 lineChartView.data = chartData 105 let xAxis = lineChartView.xAxis 106 xAxis.valueFormatter = axisFormatDelegate 107 } 108 109// func getPercentFromDatabase(page: Int) -> Results<Post>{ 110// do { 111// let realm = try Realm() 112// //1番最近のもの 113// var lastDay = realm.objects(Post.self).last?.date 114// //1週間の中で1番古い 115// var sixDaysAgo = Calendar.current.date(byAdding: .day, value: -6 + -7 * page, to: lastDay!) 116// //1週間の中で1番最近 117// var latestDay = Calendar.current.date(byAdding: .day, value: -7 * page, to: lastDay!) 118// var predicate = NSPredicate(format: "date >= %@ && date <= %@", sixDaysAgo! as CVarArg, latestDay! as CVarArg) 119// return realm.objects(Post.self).filter(predicate) 120// } catch let error as NSError { 121// fatalError(error.localizedDescription) 122// } 123// } 124 125 // --- テストのためのDateFormatterです。 126 let dateFormatter: DateFormatter = { 127 let dateFormatter = DateFormatter() 128 dateFormatter.dateStyle = .medium 129 dateFormatter.timeStyle = .none 130 dateFormatter.locale = Locale(identifier: "ja_JP") 131 return dateFormatter 132 }() 133} 134 135//X軸の値を管理 136extension GraphViewController: AxisValueFormatter { 137 138 func stringForValue(_ value: Double, axis: AxisBase?) -> String { 139 let dateFormatter = DateFormatter() 140 dateFormatter.dateFormat = "MM/dd" 141 return dateFormatter.string(from: Date(timeIntervalSince1970: value)) 142 } 143}

表示のされ方が質問欄のものと異なるところもありますが、
x軸の部分だけ参考になればと思いまして、
画像も貼り付けておきますね。

イメージ説明

軸に関するところはドキュメントの
「Customizing the axis range (min / max)」
の部分を見てみると良いかもしれませんね。
https://weeklycoding.com/mpandroidchart-documentation/axis-general/

投稿2023/04/06 01:02

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

miyuki

2023/04/08 05:28

少し離れてしまっていて返信が遅くなりました。すみません。 ご回答くださり非常にありがとうございます!ご教授いただきました通り、ChartViewのAxisMaximumとAxisMinimumに該当する日付を入れたところ、希望通りに動きました! 大変助かりました。コメントもくださっていたようで本当にありがとうございます
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問