I am trying to implement a tab-layout LazyVGrid that will contain three different data types. For this, I have taken a Single scrollView and have created multiple LazyVGrid to accommodate this data.
The problem I am facing is, that whenever a list from tab 1 is scrolled, the list from the tab scrolls at the same offset.
There are two different solutions I have already tried -
Below is what my code looks like and it'd be great if someone could help me realize how I can fix this.
Please find my code and current output. I expect that when I switch tabs and come back, the ScrollView offset remains at the position where I left it.
import SwiftUI
enum SearchType: String, CaseIterable {
case movie = "Movies"
case tv = "TV Shows"
case people = "People"
}
struct SearchContainerView: View {
@ObservedObject var viewModel = SearchViewModel()
@State var currentSelectedSearchType: SearchType = .movie
let array: [String] = ["The", "above", "works", "great", "when", "you", "know", "where", "in", "the", "array", "the", "value" ,"is", "that", "is", "when" ,"you", "know", "its", "index", "value", "As", "the", "index", "values", "begin", "at" ,"0", "the" ,"second", "entry", "will", "be", "at", "index", "1"]
var body: some View {
NavigationView {
VStack {
HStack {
ForEach(SearchType.allCases, id: \.self) { type in
HStack {
Spacer()
Text(type.rawValue)
.font(.title3)
.onTapGesture {
self.currentSelectedSearchType = type
}
Spacer()
}
.padding(5)
.background(currentSelectedSearchType == type ? Color.gray : Color.clear)
.cornerRadius(10)
}
.background(Color.gray.opacity(0.5))
.cornerRadius(10)
.padding(.horizontal)
}
ScrollView {
switch currentSelectedSearchType {
case .movie:
LazyVGrid(columns: [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible())
], content: {
ForEach(array, id: \.self) {
Text($0).font(.largeTitle).bold().frame(width: UIScreen.main.bounds.width / 3, height: 100, alignment: .center)
}
})
case .tv:
LazyVGrid(columns: [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible())
], content: {
ForEach(array, id: \.self) {
Text($0).font(.largeTitle).bold().frame(width: UIScreen.main.bounds.width / 3, height: 100, alignment: .center)
}
})
case .people:
LazyVGrid(columns: [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible())
], content: {
ForEach(array, id: \.self) {
Text($0).font(.largeTitle).bold().frame(width: UIScreen.main.bounds.width / 3, height: 100, alignment: .center)
}
})
}
}
}
}
}
}
Current Output -
<img src="https://i.imgur.com/TgkIXYo.mp4" alt="this slowpoke moves" width="250" />
I understand you want to switch between the different GridViews, but they should keep their individual scroll position.
To achieve that all 3 ScollViews have to stay in the view hierarchy, otherwise – as you stated – they are rebuilt and loose their position.
You can e.g. do that by putting all in a ZStack and controlling opacity (and activity) based on selection:
struct ContentView: View {
//@ObservedObject var viewModel = SearchViewModel()
@State var currentSelectedSearchType: SearchType = .movie
var body: some View {
NavigationView {
VStack {
HStack {
ForEach(SearchType.allCases, id: \.self) { type in
HStack {
Spacer()
Text(type.rawValue)
.font(.title3)
.onTapGesture {
self.currentSelectedSearchType = type
}
Spacer()
}
.padding(5)
.background(currentSelectedSearchType == type ? Color.gray : Color.gray.opacity(0.5))
.cornerRadius(10)
}
.padding(.horizontal)
}
ZStack { // here
SearchResults(type: "Movie")
.opacity(currentSelectedSearchType == .movie ? 1 : 0)
SearchResults(type: "Show")
.opacity(currentSelectedSearchType == .tv ? 1 : 0)
SearchResults(type: "Actor")
.opacity(currentSelectedSearchType == .people ? 1 : 0)
}
}
}
}
}
struct SearchResults: View {
let type: String
var body: some View {
ScrollView {
LazyVGrid(columns: [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible())
], content: {
ForEach(0..<30) {
Text("\(type) \($0)")
.font(.title).bold()
.frame(height: 100, alignment: .center)
}
})
}
}
}