Home > Back-end >  Multiple vertical ScrollViews in one SwiftUI view
Multiple vertical ScrollViews in one SwiftUI view

Time:07-18

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

CodePudding user response:

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)
                }
            })
        }
    }
}
  • Related