0

I have a problem when trying to scroll to top a ScrollView. I use a ScrollViewReader inside a SCrollView and I subscribe with .onReceive() my lazyVStack to a property @PUlished scrollToTop from my view model. I see that it receives the notification properly but my Scroll doesn't move to top. Here is my code:

struct NewsList: View {
    @ObservedObject var viewModel: NewsListViewModel
    @State var isDetailsViewShown = false
    var body: some View {
        NavigationView {
            GeometryReader { geometry in
                VStack {
                    if !viewModel.isSearching {
                        TitleView(title: "News")
                            .frame(height: geometry.size.height / 11)
                            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0)
                            .padding(.horizontal, 13)
                            .padding(.bottom, 0)
                    }
                    SearchView(searchText: $viewModel.searchText, isSearching: $viewModel.isSearching)
                        .padding([.trailing, .leading, .bottom], 13)
                    if !viewModel.isSearching {
                        TabsView(selected: $viewModel.selectedTab)
                            .padding(.horizontal, 13)
                    }
                    ScrollView {

                    ScrollViewReader { reader in
                        
                            LazyVStack(spacing: 15) {
                                ForEach(articles.indices, id: \.self) { index in
                                    NavigationLink(destination: DetailsNewsView(article: articles[index])) {
                                        NewsCell(article: articles[index])
                                            .onAppear {
                                                checkActionForLastCell(index: index)
                                            }
                                    }.buttonStyle(FlatLinkStyle())
                                    
                                }
                            }.animation(nil)
                            .padding(.horizontal, 8)
                            .onReceive(viewModel.$scrollTop) { (scrollToTop) in
                                guard let first = articles.first, scrollToTop else { return}
                                reader.scrollTo(first, anchor: .top)
                                print(scrollToTop)
                            }
                        }
                    }
                }
                .background(Color.background.edgesIgnoringSafeArea(.all))
                .navigationBarHidden(true)
                .onAppear {
                    //viewModel.getNews()
                }
            }
        }
    }
    
    var articles: [Article] {
        guard viewModel.searchedArticles.isEmpty || viewModel.searchText.count < 3 else { return viewModel.searchedArticles}
        return viewModel.tabsArticles[viewModel.selectedTab] ?? []
    }
    
    func checkActionForLastCell(index: Int) {
        if index == articles.count - 1 {
            viewModel.getNextPageNews(isPaging: true)
        }
    }
}

and inside my ViewModel I just use a timer to see if it receives the notification

class NewsListViewModel: ObservableObject {
   /..../
    @Published var scrollTop = false

    
    init() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
            self.scrollTop = true
        }
    }
}

I get printed the notification and the reader.scrollTo(first, anchor: .top) get executed... but my table is not scrolled to top.

1 Answer 1

1

I may hace the solution to your question:

The scrollTo method takes the id you give it (in this case, articles.first) and tries to find a view that has a id modifier with the same identifier. But you haven't put the id modifier on any of the views in your ScrollView. You need to use the id modifier:

.id(index)

Your code should look like this:

ScrollViewReader { reader in
    LazyVStack(spacing: 15) {
        ForEach(articles.indices, id: \.self) { index in
            NavigationLink(destination: DetailsNewsView(article: articles[index])) {
                NewsCell(article: articles[index])
                    .onAppear {
                        checkActionForLastCell(index: index)
                    }
            }
            //add line
            .id(index)
            .buttonStyle(FlatLinkStyle())
        }
    }
    .animation(nil)
    .padding(.horizontal, 8)
    .onReceive(viewModel.$scrollTop) { (scrollToTop) in
        guard let first = articles.first, scrollToTop else { return}
        reader.scrollTo(first, anchor: .top)
        print(scrollToTop)
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.