skip to Main Content

I have implemented a LazyVGrid to display movie posters, and I am trying to find a mechanism to load additional items using an asynchronous function. I have positioned a ProgressView at the end of the ForEach, which triggers an onAppear event. However, I am encountering a strange behavior where, at times, the onAppear does not get triggered. This is not related to my function, as I also use a print statement that isn’t triggered either.

Here’s a video demonstrating the issue along with my code:

enter image description here

  LazyVGrid(columns: [GridItem(.adaptive(minimum: 150))], spacing: 15) {
                    ForEach(filmObserver.movies, id: .id) { movie in
                        NavigationLink(destination: ItemDetailView(type: itemType.Film, itemId: movie.id)){
                            ItemCardView(movieData: movie)
                        }

                    }
                    if filmObserver.hasNext && !filmObserver.movies.isEmpty {
                        ProgressView()
                            .frame(width: 150, height: 200, alignment: .center)
                            .onAppear {
                                    print("refresh!!!")
                                    filmObserver.fetchItem(type: itemType.Film)
    
                            }
                    }
                }

Any insights on why onAppear might not be firing would be greatly appreciated. Thank you!

2

Answers


  1. Chosen as BEST ANSWER

    I tried to check if the last element of my forEach is reached, and now onAppear behaves as expected, but it's not very elegant. I'd rather prefer to use my ProgressView.

    LazyVGrid(columns: [GridItem(.adaptive(minimum: 150))], spacing: 15) {
                        ForEach(filmObserver.movies, id: .id) { movie in
                            NavigationLink(destination: ItemDetailView(type: itemType.Film, itemId: movie.id)){
                                ItemCardView(movieData: movie)
                            }.onAppear{
                                if movie == filmObserver.movies.last {
                                    Task{
                                        print("refresh!!!")
                                        filmObserver.fetchItem(type: itemType.Film)
                                    }
                                }
                            }
                        }
                        if filmObserver.hasNext && !filmObserver.movies.isEmpty {
                            ProgressView()
                                .frame(width: 150, height: 200, alignment: .center)
                        }
                    }
    

  2. Use a geometry reader in a background to deterministically detect when ProgressView appears on the bottom of the screen. The onAppear modifier fires within the SwiftUI lifecycle and will not always fire when expected.

    @State private var hasFetched: Bool = false
    
    ProgressView()
    .background(
        GeometryReader { geo in
            Color.clear
                .onChange(of: geo.frame(in: .global)) { newVal in
                    if !hasFetched {
                         if newVal.midY < UIScreen.main.bounds.height {
                             hasFetched = true
                             filmObserver.fetchItem(type: itemType.Film)
                        }
                    }
                }
        }
    )
    

    After your fetch operation completes and ProgressView has once again been pushed offscreen, you’ll want to reset hasFetched.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search