Hi there,
I have a TabView in page style. Inside that TabView I have a number of views, each view is populated with a model object from an array. The array is iterated to provide the chart data.
Here is the code:
TabView(selection: $displayedChartIndex) {
ForEach((0..<data.count), id: \.self) { index in
ZStack {
AccuracyLineView(graphData: tabSelectorModel.lineChartModels[index])
.padding(5)
}
.tag((index))
}
}
.tabViewStyle(.page)
.indexViewStyle(.page(backgroundDisplayMode: .always))
I am seeing odd behaviour, as I swipe left and right, occasionally the chart area shows the chart from another page in the TabView. I know the correct view is being shown as there are text elements.
See the screenshot below. The screen on the right is running iOS 17.2 and this works correctly. The screen on the left is running iOS 17.4 and the date at the top is correct which tells me that the data object is correct. However the graph is showing a chart from a different page. When I click on the chart on the left (I have interaction enabled) then it immediately draws the correct chart. If I disable the interaction then I still get the behaviour albeit the chart never corrects itself because there is no interaction!
I can reproduce this in the 17.4 simulator and it is happening in my live app on iOS17.4. This has only started happening since iOS 17.4 dropped and works perfectly in iOS 17.2 simulator and I didn't notice it in the live app when I was running 17.3.
Is this a bug and/or is there a workaround?
For info this is the chart view code, it is not doing anything clever:
struct AccuracyLineView: View {
@State private var selectedIndex: Int?
let graphData: LineChartModel
func calcHourMarkers (maxTime: Int) -> [Int] {
let secondsInDay = 86400 // 60 * 60 * 24
var marks: [Int] = []
var counter = 0
while counter <= maxTime {
if (counter > 0) {
marks.append(counter)
}
counter += secondsInDay
}
return marks
}
var selectedGraphMark: GraphMark? {
var returnMark: GraphMark? = nil
var prevPoint = graphData.points.first
for point in graphData.points {
if let prevPoint {
if let selectedIndex, let lastPoint = graphData.points.last, ((point.interval + prevPoint.interval) / 2 > selectedIndex || point == lastPoint) {
if point == graphData.points.last {
if selectedIndex > (point.interval + prevPoint.interval) / 2 {
returnMark = point
} else {
returnMark = prevPoint
}
} else {
returnMark = prevPoint
break
}
}
}
prevPoint = point
}
return returnMark
}
var body: some View {
let lineColour:Color = Color(AppTheme.globalAccentColour)
VStack {
HStack {
Image(systemName: "clock")
Text(graphData.getStartDate() + " - " + graphData.getEndDate()) // 19-29 Sept
.font(.caption)
.fontWeight(.light)
Spacer()
}
Spacer()
Chart {
// Lines
ForEach(graphData.points) { item in
LineMark(
x: .value("Interval", item.interval),
y: .value("Offset", item.timeOffset),
series: .value("A", "A")
)
.interpolationMethod(.catmullRom)
.foregroundStyle(lineColour)
.symbol {
Circle()
.stroke(Color(Color(UIColor.secondarySystemGroupedBackground)), lineWidth: 4)
.fill(AppTheme.globalAccentColour)
.frame(width: 10)
}
}
ForEach(graphData.trend) { item in
LineMark (
x: .value("Interval", item.interval),
y: .value("Offset", item.timeOffset)
)
.foregroundStyle(Color(UIColor.systemGray2))
}
if let selectedGraphMark {
RuleMark(x: .value("Offset", selectedGraphMark.interval))
.foregroundStyle(Color(UIColor.systemGray4))
}
}
.chartXSelection(value: $selectedIndex)
.chartXScale(domain: [0, graphData.getMaxTime()])
}
}
}