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

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

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

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

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

iPhone

iPhoneとは、アップル社が開発・販売しているスマートフォンです。 同社のデジタルオーディオプレーヤーiPodの機能、電話機能、インターネットやメールなどのWeb通信機能の3つをドッキングした機器です。

Q&A

解決済

1回答

1438閲覧

【Swift-Charts】SegmentedControlの値に応じて1ページあたりのデータ数を変え、スクロールできるようにしたいです

miyuki

総合スコア23

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

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

iPhone

iPhoneとは、アップル社が開発・販売しているスマートフォンです。 同社のデジタルオーディオプレーヤーiPodの機能、電話機能、インターネットやメールなどのWeb通信機能の3つをドッキングした機器です。

0グッド

0クリップ

投稿2022/03/30 09:35

前提

Realmのデータをもとにグラフを描画し、それをScrollViewを使ってスクロールできるようにしています。
https://bombrary.github.io/blog/posts/iphone-barchart-ioscharts/
こちらの記事を参考にしました。

実現したいこと

SegmentedControlの値に応じて、1ページに描画するデータの数を変え、またX軸の座標はページをスクロールするとX軸の値が0からやり直しになってしまうエラーを解消したいです。

最終的には下記画像のようにX軸の値を日付にして、またSegmentedControlで1週間・1ヶ月・1年ごとのデータをグラフに描画できるようにしたいです。(例えば「1週間」を選んだ場合、1ページに1週間分のデータが描画され、スクロールすることで別の週のグラフを見ることができる、といったイメージです)
イメージ説明

発生している問題・エラーメッセージ

このようにX軸のグリッドの数と1ページあたりのデータの数が等しい時は正しくX軸の座標が表示されるのですが、
イメージ説明
X軸のグリッドの数と1ページあたりのデータの数が異なる時、このようにX軸の座標が表示されてしまいます。またこの画像はスクロールして2枚目のグラフなのですが、X軸の値が0からスタートしてしまっています。
イメージ説明

該当のソースコード

Swift

1class GraphViewController: UIViewController, ChartViewDelegate { 2 3 @IBOutlet weak var tableView: UITableView! 4 @IBOutlet weak var scrollView: UIScrollView! 5 6 let realm = try! Realm() 7 var post: Post! 8 var percentPoints = try! Realm().objects(Post.self).sorted(byKeyPath: "date", ascending: true) 9 var numSegments = 7 10 11 var currentItems: Results<Post>! 12 var dataPoints = [Double]() 13 var dateString: String! 14 15 @IBAction func SegmentSelected(_ sender: UISegmentedControl) { 16 switch sender.selectedSegmentIndex { 17 case 0: 18 numSegments = 7 19 case 1: 20 numSegments = 30 21 default: 22 numSegments = 12 23 } 24 } 25 26 override func viewDidLoad() { 27 super.viewDidLoad() 28 29 //dataPointsに値を代入する 30 var i = 0 31 repeat{ 32 dataPoints.append(Double(percentPoints[i].percent)!) 33 i += 1 34 } while i < percentPoints.count 35 36 //④lineChartViewにlineChartDataを詰める 37 let lineChartView = createLineChartView(data: dataPoints) 38 self.view.addSubview(lineChartView) 39 40 scrollView.frame = CGRect(x: (UIScreen.main.bounds.size.width - UIScreen.main.bounds.size.width * 0.8) / 2, y: 70, width: UIScreen.main.bounds.size.width * 0.8, height: UIScreen.main.bounds.size.width * 0.7) 41 42 let contentsView = createContentsView(of: dataPoints, percentCountPerPage: numSegments) 43 scrollView.addSubview(contentsView) 44 scrollView.contentSize = contentsView.frame.size 45 scrollView.isPagingEnabled = true 46 } 47 48 //ページをスクロールできるようにする 49 func createContentsView(of data: [Double], percentCountPerPage: Int) -> UIView { 50 //1ページあたりのデータ(配列) 51 let itemsPerPage = stride(from: 0, to: data.count, by: percentCountPerPage).map { 52 Array(data[$0 ..< min($0 + percentCountPerPage, data.count)]) 53 } 54 let contentsView = UIView(frame: CGRect(x: 0, y: 0, width: scrollView.frame.width * CGFloat(itemsPerPage.count), height: scrollView.frame.height)) 55 for (i, data) in itemsPerPage.enumerated() { 56 let lineChartView = createLineChartView(data: data) 57 let percent = CGFloat(data.count) / CGFloat(itemsPerPage[0].count) 58 lineChartView.frame = CGRect( 59 x: scrollView.frame.width * CGFloat(i), 60 y: 0, 61 width: scrollView.frame.width * percent, 62 height: scrollView.frame.height 63 ) 64 print(itemsPerPage[i]) 65 contentsView.addSubview(lineChartView) 66 } 67 return contentsView 68 } 69 70 //dataPointsをもとにLineChartViewを作る 71 func createLineChartView(data:[Double]) -> LineChartView{ 72 let lineChartView = LineChartView() 73 lineChartView.delegate = self 74 //y軸の最大値・最小値 75 lineChartView.leftAxis.axisMaximum = 100 76 lineChartView.leftAxis.axisMinimum = -50 77 //y軸のラベルの数 78 lineChartView.leftAxis.labelCount = 7 79 //右側のラベルを非表示 80 lineChartView.rightAxis.enabled = false 81 lineChartView.legend.enabled = false 82 lineChartView.pinchZoomEnabled = false 83 lineChartView.doubleTapToZoomEnabled = false 84 lineChartView.extraTopOffset = 20 85 lineChartView.xAxis.labelPosition = .bottom 86 87 //dataPointsを渡す 88 lineChartView.data = createLineChartData(of: data) 89 return lineChartView 90 } 91 92 func createLineChartData(of data: [Double]) -> LineChartData{ 93 //①dataEntriesにデータを詰める 94 var dataEntries = [ChartDataEntry]() 95 for (xValue, yValue) in data.enumerated() { 96 let dataEntry = ChartDataEntry(x: Double(Int(xValue)), y: yValue) 97 dataEntries.append(dataEntry) 98 } 99 //②dataEntriesからlineChartDataSetを作成 100 let lineChartDataSet = LineChartDataSet(entries: dataEntries, label: "percentChart") 101 lineChartDataSet.lineWidth = 5.0 102 lineChartDataSet.mode = .linear 103 //③lineChartDataSetからLineChartDataを作成 104 let lineChartData = LineChartData(dataSet: lineChartDataSet) 105 return lineChartData 106 }

補足情報(FW/ツールのバージョンなど)

Xcodeのバージョンは最新です。
長文失礼しました。ご回答いただけますと幸いです。よろしくお願いいたします。

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

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

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

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

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

guest

回答1

0

ベストアンサー

次のデモのコードなども参考に修正してみました。
(// ***** コメント *****のような箇所が主な修正です)
(Realmなどは直接的に関係ないと思いましたので、再現確認の際に削除しています)

https://github.com/danielgindi/Charts/blob/master/ChartsDemo-iOS/Swift/Demos/LineChartTimeViewController.swift

X軸のグリッドの数と1ページあたりのデータの数が異なる時、このようにX軸の座標が表示されてしまいます。

x軸のラベルが小数点以下で中途半端に表示されてしまうようでしたので、granularityに1を指定して小数点以下が表示されないようにしたところ、うまく表示されている感じでした。
x軸が時間になる場合はこの値も変える必要があるかもしれません。

またこの画像はスクロールして2枚目のグラフなのですが、X軸の値が0からスタートしてしまっています。

x軸のデータを作る際にページのインデックスが考慮されていないようでしたので、ページのインデックスを考慮して連番にしてみました。
ただ、これは「最終的には下記画像のようにX軸の値を日付にして、」のような形になると、不要かもしれません。

swift

1import Charts 2import UIKit 3 4class ViewController: UIViewController, ChartViewDelegate { 5 6 @IBOutlet weak var scrollView: UIScrollView! 7 8 var dataPoints = [ 9 100.0, 10 100.0, 11 0.0, 12 100.0, 13 1.0, 14 0.0, 15 45.0, 16 28.0, 17 60.0 18 ] 19 var numSegments = 7 20 21 @IBAction func SegmentSelected(_ sender: UISegmentedControl) { 22 switch sender.selectedSegmentIndex { 23 case 0: 24 numSegments = 7 25 case 1: 26 numSegments = 30 27 default: 28 numSegments = 12 29 } 30 } 31 32 override func viewDidLoad() { 33 super.viewDidLoad() 34 35 //④lineChartViewにlineChartDataを詰める 36 let lineChartView = createLineChartView(data: dataPoints, page: 0) 37 self.view.addSubview(lineChartView) 38 39 scrollView.frame = CGRect(x: (UIScreen.main.bounds.size.width - UIScreen.main.bounds.size.width * 0.8) / 2, y: 70, width: UIScreen.main.bounds.size.width * 0.8, height: UIScreen.main.bounds.size.width * 0.7) 40 41 let contentsView = createContentsView(of: dataPoints, percentCountPerPage: numSegments) 42 scrollView.addSubview(contentsView) 43 scrollView.contentSize = contentsView.frame.size 44 scrollView.isPagingEnabled = true 45 } 46 47 //ページをスクロールできるようにする 48 func createContentsView(of data: [Double], percentCountPerPage: Int) -> UIView { 49 //1ページあたりのデータ(配列) 50 let itemsPerPage = stride(from: 0, to: data.count, by: percentCountPerPage).map { 51 Array(data[$0 ..< min($0 + percentCountPerPage, data.count)]) 52 } 53 let contentsView = UIView(frame: CGRect(x: 0, y: 0, width: scrollView.frame.width * CGFloat(itemsPerPage.count), height: scrollView.frame.height)) 54 for (i, data) in itemsPerPage.enumerated() { 55 let lineChartView = createLineChartView(data: data, page: i) 56 let percent = CGFloat(data.count) / CGFloat(itemsPerPage[0].count) 57 lineChartView.frame = CGRect( 58 x: scrollView.frame.width * CGFloat(i), 59 y: 0, 60 width: scrollView.frame.width * percent, 61 height: scrollView.frame.height 62 ) 63 print(itemsPerPage[i]) 64 contentsView.addSubview(lineChartView) 65 } 66 return contentsView 67 } 68 69 //dataPointsをもとにLineChartViewを作る 70 func createLineChartView(data:[Double], page: Int) -> LineChartView{ 71 let lineChartView = LineChartView() 72 lineChartView.delegate = self 73 //y軸の最大値・最小値 74 lineChartView.leftAxis.axisMaximum = 100 75 lineChartView.leftAxis.axisMinimum = -50 76 //y軸のラベルの数 77 lineChartView.leftAxis.labelCount = 7 78 //右側のラベルを非表示 79 lineChartView.rightAxis.enabled = false 80 lineChartView.legend.enabled = false 81 lineChartView.pinchZoomEnabled = false 82 lineChartView.doubleTapToZoomEnabled = false 83 lineChartView.extraTopOffset = 20 84 lineChartView.xAxis.labelPosition = .bottom 85 86 // ***** x軸の間隔の最小を1にする ***** 87 let xAxis = lineChartView.xAxis 88 xAxis.granularity = 1 89 90 //dataPointsを渡す 91 lineChartView.data = createLineChartData(of: data, page: page) 92 return lineChartView 93 } 94 95 func createLineChartData(of data: [Double], page: Int) -> LineChartData{ 96 //①dataEntriesにデータを詰める 97 var dataEntries = [ChartDataEntry]() 98 for (xValue, yValue) in data.enumerated() { 99 // ***** ページも考慮して連番にする ***** 100 let dataEntry = ChartDataEntry(x: Double(Int(xValue) + (page * numSegments)), y: yValue) 101 dataEntries.append(dataEntry) 102 } 103 //②dataEntriesからlineChartDataSetを作成 104 let lineChartDataSet = LineChartDataSet(entries: dataEntries, label: "percentChart") 105 lineChartDataSet.lineWidth = 5.0 106 lineChartDataSet.mode = .linear 107 //③lineChartDataSetからLineChartDataを作成 108 let lineChartData = LineChartData(dataSet: lineChartDataSet) 109 return lineChartData 110 } 111}

投稿2022/03/30 22:10

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

miyuki

2022/04/01 14:06 編集

ご回答いただきありがとうございます!具体的なソースコードまで教えていただき非常に助かりました。 ご指摘いただいた通り、コードを書き換えた所、無事エラーを解決することができました! ScrollViewのグラフの何ページ目なのか保持する値が必要だったのですね!ご丁寧にご回答くださり本当にありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.51%

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

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

質問する

関連した質問