I have encountered an issue while implementing the scrollTo mechanism for a lengthy list of products displayed using LazyVGrid
in SwiftUI.
Does anyone have a potential solution for this problem?
Below is the code to reproduce the issue:
import SwiftUI
struct Product: Identifiable {
let id = UUID()
let name: String
let price: Double
let imageName: String
}
struct Category: Identifiable {
let id = UUID()
let name: String
let products: [Product]
}
class MockData {
static func generate() -> [Category] {
var categories = [Category]()
for i in 1...30 {
let productCount = Int.random(in: 5...50)
var products = [Product]()
for j in 1...productCount {
products.append(Product(name: "Product \(j)", price: Double.random(in: 10...100), imageName: "photo"))
}
categories.append(Category(name: "Category \(i)", products: products))
}
return categories
}
}
struct ContentView: View {
let categories = MockData.generate()
@State private var selectedCategoryIndex: Int? = nil
var body: some View {
NavigationView {
ScrollViewReader { scrollView in
VStack {
ScrollView(.horizontal) {
HStack {
ForEach(categories.indices, id: \.self) { index in
Button(action: {
withAnimation {
scrollView.scrollTo(categories[index].id, anchor: .top)
}
}) {
Text(categories[index].name)
.padding()
.background(Color.gray.opacity(0.2))
.cornerRadius(8)
}
}
}
.padding()
}
ScrollView {
LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())]) {
ForEach(categories) { category in
Section(header: Text(category.name).font(.headline).padding()) {
ForEach(category.products) { product in
VStack {
Image(systemName: product.imageName)
.resizable()
.frame(width: 120, height: 120)
Text(product.name)
Text("$\(product.price, specifier: "%.2f")")
.font(.subheadline)
.foregroundColor(.gray)
}
.padding()
.background(Color.white)
.cornerRadius(8)
.shadow(radius: 2)
}
}
}
}
}
}
}
}
}
}
EDIT: I found that it also happens with LazyVStack
.
ScrollView {
LazyVStack {
ForEach(categories.indices, id: \.self) { index in
Section(header: Text(categories[index].name)
.font(.headline)
.padding()
.id(categories[index].id)) {
ForEach(categories[index].products) { product in
HStack {
Image(systemName: "photo")
.resizable()
.frame(width: 50, height: 50)
.padding()
VStack(alignment: .leading) {
Text(product.name)
Text(String(format: "$%.2f", product.price))
}
Spacer()
}
.background(Color.white)
.cornerRadius(10)
.shadow(radius: 2)
.padding(.horizontal)
}
}
}
}
.padding(.bottom, 50)
}
I would expect that after tapping on category my list will be scrolled like this:
But when going through categories and tapping on them I randomly end up with some offset, eg. it looks then like this:
Any Idea how I can fix it?
NavigationView
.