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

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

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

Q&A

1回答

981閲覧

折れ線グラフのスクロールとズーム

rikaham0321

総合スコア2

0グッド

0クリップ

投稿2022/06/16 19:15

下記のコードについて質問です。
現在、体重を入力して保存すると折れ線グラフが描かれるコードを書いています。
数回の入力ならいいのですが、多数回入力するとグラフの数字や〇が多くなり大変見づらくなってしまいます。
なので、①一定の間隔を開けて横スクロールにする
または、②ズームした状態でアプリを閉じて再度開いてもそこから始まる
この①か②を実現したいと思っています。
理想はどちらも可能にすることですが、私の知識や発想力では出来ませんでした。
アイデアや知識などお貸しいただければと思います。
よろしくお願いします。

swift

1import SwiftUI 2import Charts 3 4struct WeightPage2: View{ 5 @State var weight = "" 6 @State var weightarray:[String] 7 8 var body: some View{ 9 TextField("今日の体重",text:$weight).textFieldStyle(RoundedBorderTextFieldStyle()).frame(width:120).overlay(RoundedRectangle(cornerRadius:1).stroke(Color.black, lineWidth:1)) 10 Button(action:{weightarray.append(weight);UserDefaults.standard.set(weightarray, forKey: "weightkey")}){ 11 Text("保存する") 12 } 13 LineChart(weightarray:$weightarray).frame(width:360 ,height: 400) 14 } 15 init() { 16 let w = UserDefaults.standard.array(forKey: "weightkey") as? [String] ?? [] 17 _weightarray = State(initialValue: w) 18 } 19} 20 21struct LineChart : UIViewRepresentable { 22 typealias UIViewType = LineChartView 23 @Binding var weightarray:[String] 24 25 func makeUIView(context: Context) -> LineChartView { 26 let lineChartView = LineChartView() 27 lineChartView.data = setData() 28 29 return lineChartView 30 } 31 func updateUIView(_ uiView: LineChartView, context: Context) { 32 uiView.data = setData() 33 } 34 func setData() -> LineChartData{ 35 let dataPoint = getDataPoints() 36 let set = LineChartDataSet(entries: dataPoint, label: "My data") 37 let data = LineChartData(dataSet: set) 38 return data 39 } 40 func getDataPoints() -> [ChartDataEntry] { 41 var dataPoints: [ChartDataEntry] = [] 42 for count in 0..<weightarray.count{ 43 dataPoints.append(ChartDataEntry(x:Double(count),y: Double(weightarray[count]) ?? 0)) 44 } 45 return dataPoints 46 } 47} 48 49struct WeightPage2_Previews: PreviewProvider { 50 static var previews: some View { 51 WeightPage2() 52 } 53}

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

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

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

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

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

guest

回答1

0

①のイメージがちょっとわからなかったので・・
②のアイデア的なコードを書いてみます。
ちょっと調べてみての思いつきのコードですので、デバッグしながらズームの再現を探ってみてもらえたらと思います。
変更したところに// *****のようなコメントを入れています。

swift

1struct LineChart : UIViewRepresentable { 2 typealias UIViewType = LineChartView 3 @Binding var weightarray:[String] 4 // ***** Bindingは呼び出しもとのビューでUserDefaultsなどで保持すると良いかもしれません 5 @Binding var scaleX: CGFloat 6 @Binding var scaleY: CGFloat 7 @Binding var x: CGFloat 8 @Binding var y: CGFloat 9 10 // ***** ChartViewDelegateを使うためにCoordinatorを生成します 11 func makeCoordinator() -> Coordinator { 12 Coordinator(self) 13 } 14 func makeUIView(context: Context) -> LineChartView { 15 let lineChartView = LineChartView() 16 lineChartView.data = setData() 17 // ***** delegateを設定します 18 lineChartView.delegate = context.coordinator 19 // ***** ビューを生成する際に次のような感じでズームを再現できるかもしれません 20// lineChartView.zoom(scaleX: scaleX, scaleY: scaleY, x: x, y: y) 21 return lineChartView 22 } 23 func updateUIView(_ uiView: LineChartView, context: Context) { 24 uiView.data = setData() 25 } 26 func setData() -> LineChartData{ 27 let dataPoint = getDataPoints() 28 let set = LineChartDataSet(entries: dataPoint, label: "My data") 29 let data = LineChartData(dataSet: set) 30 return data 31 } 32 func getDataPoints() -> [ChartDataEntry] { 33 var dataPoints: [ChartDataEntry] = [] 34 for count in 0..<weightarray.count{ 35 dataPoints.append(ChartDataEntry(x:Double(count),y: Double(weightarray[count]) ?? 0)) 36 } 37 return dataPoints 38 } 39 // ***** ChartViewDelegateを処理するためのクラスです 40 class Coordinator: NSObject, ChartViewDelegate { 41 var parent: LineChart 42 init(_ lineChart: LineChart) { 43 parent = lineChart 44 } 45 // ***** ズームしたときのデリゲートです 46 func chartScaled(_ chartView: ChartViewBase, scaleX: CGFloat, scaleY: CGFloat) { 47 // ***** スケールと中心座標を保持するようにすると良いかもしれません 48 // ***** viewPortHandlerは内部実装を良く知った上で使わないと予期せぬ動作をするみたいです・・ 49 parent.scaleX = scaleX 50 parent.scaleY = scaleY 51 parent.x = chartView.viewPortHandler.transX 52 parent.y = chartView.viewPortHandler.transY 53 print("\(scaleX), \(scaleY), \(chartView.viewPortHandler.transX), \(chartView.viewPortHandler.transY)") 54 } 55 // ***** スクロールしたときのデリゲートです 56 func chartTranslated(_ chartView: ChartViewBase, dX: CGFloat, dY: CGFloat) { 57 // ***** スクロールしたときにも中心座標を保持すると良いかもしれません 58 print("\(dX), \(dY)") 59 } 60 } 61}

Chartsのドキュメントを探してみたのですが、Swiftのはなくて、Androidのを参照する感じなのですね。
次のあたりを見ると良いかもしれません。
https://weeklycoding.com/mpandroidchart-documentation/modifying-the-viewport/

あとはGitHubのコードを探ったりするのも良いかもしれません。
https://github.com/danielgindi/Charts

SwiftUIからUIKitのデリゲートを使う方法などはSwiftUIのチュートリアルを見ると良いかもしれません。
https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit


追記です。
コメントありがとうございます。

すでにズームはできていたのですね。

ちょっと座標をどう扱っているのか難しいですね・・
GitHubのIssueを「scroll」などのキーワードで検索してみるのも良いかもしれません。

ヒントになりそうに思ったのは、座標で扱わなくても、データのx(xはインデックスですね)とyの値を指定することで表示する位置を変えることができるみたいでした。
次の例ではデータの最後の位置を表示する感じになるみたいです。

https://github.com/danielgindi/Charts/issues/450#issuecomment-954580674

swift

1private func viewChartFromTheEnd(chart: BarChartView) { 2 let lastIndex = chart.data!.dataSets.first!.entryCount - 1 3 let lastEntry = chart.data?.dataSets.first?.entryForIndex(lastIndex) 4 chart.centerViewTo(xValue: lastEntry!.x, yValue: lastEntry!.y, axis: .right) 5}

それから見えている部分のxの最小と最大が次のように得られるみたいです。
ですので、これらを足して2で割ったものがcenter(xに相当する値)になると思います。
このインデックスを持つデータ(yに相当する値)を探して、
中心のxとyを保存しておくという感じにすれば、
chart.centerViewToで表示位置が再現できそうに思いましたがどうでしょうか。

https://github.com/danielgindi/Charts/issues/4030

swift

1print(chart.lowestVisibleX.rounded()) 2print(chart.highestVisibleX.rounded())

追記2つ目です。
1つ目の追記を書いてから気づきましたが、
この案だとx座標は再現できても、y座標は再現できませんね・・
ちょっと今は他の案が思いつきませんでした・・
ごめんなさい。

投稿2022/06/17 10:39

編集2022/06/19 09:10
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

rikaham0321

2022/06/19 06:35

丁寧なご回答ありがとうございます! 連絡が遅くなりすみません。 ズームはご回答の時点でアプリを閉じても同じ倍率のまま出来るようになっていました。 なので、座標の方をご回答を参考にデバッグしながらいろいろ試してみました。 しかし未だに出来ておらず、一度アプリを閉じると常に左下に戻ってしまいます。 CGRectを用いると左下ではない座標になったのですが、理想とする座標にはなりませんでした。 私が至らないばかりに...すみません。 貴重なお時間を割いていただきありがとうございました。
rikaham0321

2022/06/19 10:09

追記ありがとうございます! いろいろ考えていただき感謝しかありません。 多数回入力すると詰め詰めになってしまい見にくいので、見やすくなる方法を考えたのですが私の質問した①②の案が悪かったかもしれないです。 ①②の他に見やすくなる方法があればそちらで良いのですが、他の案も思いつかずすみません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問