3

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 -

  1. Create LazyVGrid as a variable - I was unable to do that since the data it will have belongs to ViewModel and also LazyVGrid is a separate view in a practical example.
  2. Use ScrollView each time - I tried doing it but every time the currently selected type changes, SwiftUI forces LazyVGrid to repopulate and shows from offset 0.

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" />

1
  • I don't see multiple ScrollView in provided code and don't see tabs... needed minimal reproducible example. Commented Jul 18, 2022 at 4:19

1 Answer 1

3

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) } }) } } } 
Sign up to request clarification or add additional context in comments.

1 Comment

This is certainly the aspect I never thought of. Thank you so much for your time and answer. It solves my problem and also I learned something new today.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.