ios - Swift Charts: How to prevent scroll position jump when loading more data dynamically - Stack Overflow

I'm implementing infinite scrolling with Swift Charts where additional historical data loads when

I'm implementing infinite scrolling with Swift Charts where additional historical data loads when scrolling near the beginning of the dataset. However, when new data is loaded, the chart's scroll position jumps unexpectedly.

Current behavior:

  • Initially loads 10 data points, displaying the latest 5
  • When scrolling backwards with only 3 points remaining off-screen, triggers loading of 10 more historical points
  • After loading, the scroll position jumps to the 3rd position of the new dataset instead of maintaining the current view

Expected behavior:

  • Scroll position should remain stable when new data is loaded
  • User's current view should not change during data loading

Here's my implementation logic using some mock data:

import SwiftUI
import Charts

struct DataPoint: Identifiable {
  let id = UUID()
  let date: Date
  let value: Double
}

class ChartViewModel: ObservableObject {
  @Published var dataPoints: [DataPoint] = []
  private var isLoading = false
  
  init() {
    loadMoreData()
  }
  
  func loadMoreData() {
    guard !isLoading else { return }
    
    isLoading = true
    
    let newData = self.generateDataPoints(
      endDate: self.dataPoints.first?.date ?? Date(),
      count: 10
    )
    
    self.dataPoints.insert(contentsOf: newData, at: 0)
    self.isLoading = false
    
    print("\(dataPoints.count) data points.")
  }
  
  private func generateDataPoints(endDate: Date, count: Int) -> [DataPoint] {
    var points: [DataPoint] = []
    let calendar = Calendar.current
    
    for i in 0..<count {
      let date = calendar.date(
        byAdding: .day,
        value: -i,
        to: endDate
      ) ?? endDate
      
      let value = Double.random(in: 0...100)
      points.append(DataPoint(date: date, value: value))
    }
    
    return points.sorted { $0.date < $1.date }
  }
}

struct ScrollableChart: View {
  @StateObject private var viewModel = ChartViewModel()
  @State private var scrollPosition: Date
  
  @State private var scrollDebounceTask: Task<Void, Never>?
  
  init() {
    self.scrollPosition = .now.addingTimeInterval(-4*24*3600)
  }
  
  var body: some View {
    Chart(viewModel.dataPoints) { point in
      BarMark(
        x: .value("Time", point.date, unit: .day),
        y: .value("Value", point.value)
      )
    }
    .chartScrollableAxes(.horizontal)
    .chartXVisibleDomain(length: 5 * 24 * 3600)
    .chartScrollPosition(x: $scrollPosition)
    .chartXScale(domain: .automatic(includesZero: false))
    .frame(height: 300)
    .onChange(of: scrollPosition) { oldPosition, newPosition in
      scrollDebounceTask?.cancel()
      
      scrollDebounceTask = Task {
        try? await Task.sleep(for: .milliseconds(300))
        
        if !Task.isCancelled {
          checkAndLoadMoreData(currentPosition: newPosition)
        }
      }
    }
  }
  
  private func checkAndLoadMoreData(currentPosition: Date?) {
    guard let currentPosition,
          let earliestDataPoint = viewModel.dataPoints.first?.date else {
      return
    }
    
    let timeInterval = currentPosition.timeIntervalSince(earliestDataPoint)
    
    if timeInterval <= 3 * 24 * 3600 {
      viewModel.loadMoreData()
    }
  }
}

I attempted to compensate for this jump by adding: scrollPosition = scrollPosition.addingTimeInterval(10 * 24 * 3600) after viewModel.loadMoreData(). However, this caused the chart to jump in the opposite direction by 10 days, rather than maintaining the current position.

What's the problem with my code and how to fix it?

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745155624a4614103.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信