Swift Charts' `chartScrollTargetBehavior` doesn't work on SDK 27

Symptom: chart (bar chart at least) doesn't snap to beginning of a bar mark when specified with chartScrollTargetBehavior. It could stop anywhere in a bar mark. In the following reproduction, I'm using a bar chart with x values being Date objects, and y values being Double. I attached a ProbeBehavior which runs ValueAlignedChartScrollTargetBehavior(matching: DateComponents(hour: 0), majorAlignment: .page)'s updateTarget, but with some debug info printed out. So essentially, I'm running .chartScrollTargetBehavior(.valueAligned(matching: DateComponents(hour: 0), majorAlignment: .page)).

Minimal reproduction

import SwiftUI
import Charts
@main struct ChartSnapApp: App { var body: some Scene { WindowGroup { TestChart() } } }
private struct TestChart: View {
    let calendar: Calendar = .current
    @State private var scrollX = Date.now
    var body: some View {
        let half = 42
        let today = calendar.startOfDay(for: .now)
        let start = calendar.date(byAdding: .day, value: -half, to: today)!
        let end = calendar.date(byAdding: .day, value: half, to: today)!

        VStack {
            Text("Scroll: \(scrollX.formatted(.iso8601)) | secondsFromMidnight: \(scrollX.timeIntervalSince(calendar.startOfDay(for: scrollX)), specifier: "%.3f")")
                .font(.caption).accessibilityIdentifier("scrollReadout")
            Chart {
                ForEach(0 ..< half * 2, id: \.self) { offset in
                    let date = calendar.date(byAdding: .day, value: offset, to: start)!
                    BarMark(x: .value("Date", date, unit: .day), y: .value("Value", 1.0))
                }
            }
            .chartXScale(domain: start ... end)
            .chartXVisibleDomain(length: 7 * 24 * 3600)
            .chartYScale(domain: 0 ... 3)
            .chartScrollableAxes(.horizontal)
            .chartScrollTargetBehavior(ProbeBehavior())
            .chartScrollPosition(x: $scrollX)
            .onChange(of: scrollX) { _, new in print("SCROLL \(new.formatted(.iso8601)) secondsFromMidnight=\(new.timeIntervalSince(calendar.startOfDay(for: new)))") }
            .environment(\.calendar, calendar)
            .padding(.all, 12)
            .accessibilityIdentifier("chart")
        }
    }
}

private struct ProbeBehavior: ChartScrollTargetBehavior {
    func updateTarget(_ target: inout ScrollTarget, context: ChartScrollTargetBehaviorContext) {
        let before = target.rect
        let orig = context.originalTarget.rect
        let beforeDate: Date? = context.chartProxy.value(atX: before.minX)
        let origDate: Date? = context.chartProxy.value(atX: orig.minX)
        
        ValueAlignedChartScrollTargetBehavior(matching: DateComponents(hour: 0), majorAlignment: .page).updateTarget(&target, context: context)
        
        let afterDate: Date? = context.chartProxy.value(atX: target.rect.minX)
        let domain = context.chartProxy.xDomain(dataType: Date.self)
        let zero = context.chartProxy.value(atX: 0, as: Date.self)
        let todayPosition = context.chartProxy.position(forX: Calendar.current.startOfDay(for: .now))
        let line = "PROBE calendar=\(Calendar.current.identifier) timezone=\(Calendar.current.timeZone.identifier) plotSize=\(context.chartProxy.plotSize) xDomain=\(domain) valueAtZero=\(String(describing: zero)) todayPosition=\(String(describing: todayPosition)) velocity=\(context.velocity) originalRect=\(orig) beforeRect=\(before) afterRect=\(target.rect) originalDate=\(String(describing: origDate)) beforeDate=\(String(describing: beforeDate)) afterDate=\(String(describing: afterDate))\n"
        
        print(line)
        
        let url = URL.documentsDirectory.appending(path: "probe.log")
        if !FileManager.default.fileExists(atPath: url.path) { FileManager.default.createFile(atPath: url.path, contents: nil) }
        if let handle = try? FileHandle(forWritingTo: url) {
            handle.seekToEndOfFile()
            handle.write(Data(line.utf8))
            try? handle.close()
        }
    }
}

Comparing video, left iOS 26.5 simulator, right iOS 27.0 simulator

https://jumpshare.com/s/SVdDV27qHlXdbxMp8mWA

In addition, in the ChartScrollTargetBehavior's updateTarget, the context argument doesn't seem to return a valid chartProxy. It gives

  context.chartProxy.plotSize                  // (0, 0)
  context.chartProxy.xDomain(dataType: Date.self) // []
  context.chartProxy.value(atX: x, as: Date.self) // nil

submitted as FB24729736

Swift Charts' &#96;chartScrollTargetBehavior&#96; doesn't work on SDK 27
 
 
Q