Search code examples
iosswiftswiftuiswiftui-foreach

Execute code when ForEach finish - swiftUI


I want to know if there is a method that executes a given code on the event forEach funtion finish (like OnAppear or similar methods provided by apple) and in case no method is available, how can i executed code when ForEach finish.

I have the following code:

ScrollView {
    ScrollViewReader { value in
        VStack  {
            let tweets = getFilteredTweets()
            ForEach((0..<tweets.count), id: \.self) { index in
                NavigationLink(destination: TweetWebView(
                    tweetId: tweets[index].id,
                    accountId: self.viewModel.viewState.selectedAccounts.first(where: {
                        $0.screenName == tweets[index].screenName
                    })?.screenName?.lowercased()
                )) {
                    VStack {
                        TweetView(
                            style: .withNoImage,
                            withHeight: true,
                            account: self.viewModel.viewState.selectedAccounts.first(where: {
                                $0.screenName == tweets[index].screenName
                            }),
                            tweet: tweets[index]
                        )
                            .frame(maxHeight: .infinity)
                            .onTapGesture {
                                viewModel.triggerEvent(.setTweetSelectedIndex(index: index))
                            }
                    }
                }
                .buttonStyle(PlainButtonStyle())
            }
            .onAppear {
                value.scrollTo(viewModel.viewState.tweetSelectedIndex ?? 0)
            }
        }
    }
}

The code I want to execute in the event ForEach finish:

value.scrollTo(viewModel.viewState.tweetSelectedIndex ?? 0)

Solution

  • ForEach doesn't have any appearance of its own. It represents a collection of views, and it is up to the container of the ForEach to decide how to show that collection. So what does onAppear even mean when attached to ForEach? Does it flow down to each view in the collection? It's better to avoid relying on the behavior of such a weird combination.

    Attach the onAppear to the containing VStack instead.

    The scrollTo method takes the id you give it (in this case, viewModel.viewState.tweetSelectedIndex ?? 0) 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:

    ScrollView {
        ScrollViewReader { value in
            VStack  {
                let tweets = getFilteredTweets()
                ForEach(0 ..< tweets.count, id: \.self) { index in
                    NavigationLink(destination: TweetWebView(
                        tweetId: tweets[index].id,
                        accountId: self.viewModel.viewState.selectedAccounts.first(where: {
                            $0.screenName == tweets[index].screenName
                        })?.screenName?.lowercased()
                    )) {
                        // blah blah blah
                    }
                    .buttonStyle(PlainButtonStyle())
                    .id(index)
    // ADD THIS     ^^^^^^^^^^
                }
            }
            .onAppear {
    //      ^^^^^^^^^    MOVE THIS ONTO THE VSTACK
                value.scrollTo(viewModel.viewState.tweetSelectedIndex ?? 0)
            }
        }
    }