I'm using .chartAngleSelection to grab the angle of the taps on a SectorMark. When using .chartAngleSelection, it does not register "fast" taps on the screen. You have to hold your finger on the screen for longer than a tap for it to be registered. I've tried the same code with .chartXSelection and .chartYSelection and there is no tap delay.
I'm not sure if this is because .chartAngleSelection just came out of beta but it definitely impacts UX.
Explore pie charts and interactivity in Swift Charts
RSS for tagDiscuss the WWDC23 Session Explore pie charts and interactivity in Swift Charts
Posts under wwdc2023-10037 tag
7 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
Does anyone know of to get the currently visible data items in chart (e.g. BarMarks) on iOS 17 when using chartScrollableAxes.
From the WWDC23 video of session "Explore pie charts and interactivity in Swift Charts" at time 8:18 you see that the "Total sales" lists the visible time frame (e.g. May 4 - June 4, 2023) and a few seconds late when the chart scrolls the values do update.
When using the new iOS “chartScrollableAxes” feature annotation that should show on top of the chart view no longer work. When disabling “chartScrollableAxes” everything works again.
Filed as FB12584128
import SwiftUI
import Charts
class ChartDataEntry:Identifiable {
var date:Date
var value:Double
init(date:Date, value: Double) {
self.date=date
self.value=value
}
}
struct ContentView: View {
@State var rawSelectedDate: Date?
var chartData = [ChartDataEntry(date: Date().yesterday.yesterday.yesterday.yesterday.startOfDay, value: 10.0),
ChartDataEntry(date: Date().yesterday.yesterday.yesterday.startOfDay, value: 20.0),
ChartDataEntry(date: Date().yesterday.yesterday.startOfDay, value: 30.0),
ChartDataEntry(date: Date().yesterday.startOfDay, value: 40.0),
ChartDataEntry(date: Date().startOfDay, value: 50.0)]
var body: some View {
VStack {
Chart(chartData) { entry in
BarMark(
x: .value("Day", entry.date, unit: .day),
y: .value("Values", entry.value)
)
if let selectedDate {
RuleMark( x: .value("Selected", selectedDate, unit: .day))
.foregroundStyle(Color.gray.opacity(0.3))
.offset(yStart: -10)
.zIndex(-1)
.annotation(position: .top, spacing: -10,
overflowResolution: .init(
x: .fit(to: .chart),
y: .disabled
)
) {
Text("Longer Sample Text")
}
}
}
.chartScrollableAxes(.horizontal) // <<--- when this is disabled the annotation will be shown correctly
.chartXSelection(value: $rawSelectedDate)
.frame(height: 300)
}
.padding()
}
var selectedDate: Date? {
guard let rawSelectedDate else { return nil }
return chartData.first(where: {
let endOfDay = $0.date.endOfDay
return ($0.date ... endOfDay).contains(rawSelectedDate)
})?.date
}
}
#Preview {
ContentView()
}
extension Date {
var yesterday: Date {
return Calendar.current.date(byAdding: .day, value: -1, to: self)!
}
var startOfDay: Date {
return Calendar.current.startOfDay(for: self)
}
var endOfDay: Date {
var components = DateComponents()
components.day = 1
components.second = -1
return Calendar.current.date(byAdding: components, to: startOfDay)!
}
}
I like to have a scrollable chart with chartXSelection. How can that be accomplished?
As soon as I have .chartXSelection the chart doesn't really scroll any more. I'm looking for some sort of delayed chartXSelection gesture, where the chartXSelection kicks in after a short delay of 1-2 seconds.
The Apple Heath App does support this very nicely.
Hi There,
Same data shows different line drawing. What I am missing here ? Each time I click to see chart, its drawing is different.
Chart{
ForEach(data)
{ series in
ForEach(series.data){ entry in
LineMark(
x: .value("Day", entry.day),
y: .value("Amount", entry.amount)
) .foregroundStyle(by: .value("Doc", series.Doc))
.symbol(by: .value("Doc", series.Doc))
PointMark(
x: .value("Day", entry.day),
y: .value("Sales", entry.amount)
)
.foregroundStyle(.purple)
.symbolSize(symbolSize)
}
.lineStyle(StrokeStyle(lineWidth: lineWidth))
}
}
.chartLegend(position: .top)
.chartYScale(domain: .automatic(includesZero: false))
.chartXAxis {
AxisMarks(values: .automatic(roundLowerBound: false, roundUpperBound: false)) { value in
if value.index < value.count - 1 {
AxisValueLabel()
}
AxisTick()
AxisGridLine()
}
}
I have a scrollable barmark chart that is introduced in iOS 17 but I have problem to set the initial horizontal scroll position. I want to use the ".chartScrollPosition(initialX:..." but initialX needs to be of type Plottable I can't understand how I can define that. Here is an example, I want it to scroll to the far right (newest days). I show seven days at a time.
import SwiftUI
import Charts
struct ContentView: View {
let stepDays: [StepDay] = [
StepDay(date: Calendar.current.date(byAdding: .day, value: -10, to: Date())!, steps: 12342),
StepDay(date: Calendar.current.date(byAdding: .day, value: -9, to: Date())!, steps: 8345),
StepDay(date: Calendar.current.date(byAdding: .day, value: -8, to: Date())!, steps: 7656),
StepDay(date: Calendar.current.date(byAdding: .day, value: -7, to: Date())!, steps: 4564),
StepDay(date: Calendar.current.date(byAdding: .day, value: -6, to: Date())!, steps: 2344),
StepDay(date: Calendar.current.date(byAdding: .day, value: -5, to: Date())!, steps: 7654),
StepDay(date: Calendar.current.date(byAdding: .day, value: -4, to: Date())!, steps: 4532),
StepDay(date: Calendar.current.date(byAdding: .day, value: -3, to: Date())!, steps: 6788),
StepDay(date: Calendar.current.date(byAdding: .day, value: -2, to: Date())!, steps: 4567),
StepDay(date: Calendar.current.date(byAdding: .day, value: -1, to: Date())!, steps: 5678),
StepDay(date: Date(), steps: 1234)]
var body: some View {
Chart {
ForEach(stepDays, id: \.date) { stepDay in
BarMark(
x: .value("Day", stepDay.weekDay),
y: .value("Steps", stepDay.steps)
)
}
}
.chartScrollableAxes(.horizontal)
.chartXVisibleDomain(length: 7)
// .chartScrollPosition(initialX: <#T##Plottable#>) // I want to scroll to far right so that I can see the newest days, can I do that with chartScrollPosition(initialX:... ???
}
}
struct StepDay {
var date: Date
var weekDay: String
var steps: Int
init(date: Date, steps: Int) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "d/M"
self.date = date
self.weekDay = dateFormatter.string(from: date)
self.steps = steps
}
}