Autoscroll Swift Chart

218 Views Asked by At

I'm having a play with the new swift charts capability built into iOS 17. The new version provides scrolling capabilities. I've managed to get a sample of this working and the chart can be manually scrolled horizontally by specifying .chartScrollableAxes(.horizontal). However I need my to chart to scroll programatically along the xAxis.

Essentially I need to have a button trigger the chart to scroll and stop again when the button is clicked again. This will require a level of customisation also, for example the ability to specify the speed of the scrolling.

What I've discovered.

I have looked for a way to specify the xPosition of the chart but have only discovered a way to read it using .chartScrollPosition.

Can anybody point me in the right direction?

import SwiftUI
import Charts



struct ChartData: Identifiable {
    let id = UUID()
    
    var title: String
    var startTime: Double
    var endTime: Double
    
    init(startTime: Double, duration: Double, title: String) {
        self.title = title
        self.startTime = startTime
        self.endTime = startTime + duration
    }
}

var data2: [ChartData] = [
    ChartData(startTime: 2.0, duration: 5.0, title: "First"),
    ChartData(startTime: 750.0, duration: 30.6, title: "Second")
]

struct ContentView: View {
    @State var chartPosition = Double(0.0)
    @State var chartScale = Double(10.0)
    @State var markPosition = Double(0.0)
    @State var duration = Double(750.0 + 30.6)
    
    var body: some View {
        VStack {
            HStack{
                Text("Position: \(chartPosition)")
            }
            HStack{
                Slider(value: $chartScale, in: 1...360)
                Text("\(chartScale)")
            }
            HStack{
                Slider(value: $markPosition, in: 0...duration)
                Text("\(markPosition)")
            }
            Chart{
                ForEach(data2) { item in
                    RectangleMark(
                        xStart: .value("TL Start", item.startTime),
                        xEnd: .value("End", item.endTime),
                        y: .value("Title", item.title)
                    )
                }
            }
            .chartXVisibleDomain(length: chartScale)
            .chartScrollableAxes(.horizontal)
            .chartYAxis(.hidden)
            .chartScrollPosition(x: $chartPosition)
        
        }
        .padding()
    }
    
}
1

There are 1 best solutions below

0
workingdog support Ukraine On BEST ANSWER

For the ... chart to scroll programatically along the xAxis, you could try this approach, simply updating the chartPosition value.

For example, recycling your current slider,

 Slider(value: $markPosition, in: 0...duration)
     .onChange(of: markPosition) {
         chartPosition = markPosition  // <--- here
     }

as you change the slider, the chart will scroll to the chosen position.

Or a specific slider

Slider(value: $chartPosition, in: 0...duration)

You can use the same approach using a Button, just update the chartPosition value. Note you can futher customise the scrolling using chartScrollTargetBehavior.

EDIT-1:

to have a button trigger the chart to scroll and stop again when the button is clicked again, you could use something like this example code:

declare:

 @State var doScroll = false
 @State var speed = 2.0

and in the body:

 HStack {
     Button("scroll >") {
         doScroll.toggle()
         Task { @MainActor in
             for _ in 0..<50  {  // <-- todo
                 if doScroll {
                     chartPosition += speed  // <-- more logic here
                     try? await Task.sleep(for: .seconds(0.1))  // <-- adjust
                 } else {
                     break
                 }
             }
         }
     }.buttonStyle(.bordered)
 }