7

I want to show a List, where each row appears with an opacity animation and with an increasing delay. So 1st row should appear after 0.1 seconds, second after 0.3 seconds, third after 0.5 seconds etc.

I tried the following, but it does not work, as all rows appear at once and without animation.

Any tips would be much appreciated!

struct GuideListView: View {

    @State var showListItems = false
    @State var animationDelay = 0.1
    // definitions of viewRouter, data etc.

    var body: some View {

        VStack {

            // other items, navLink etc.

            List {
                
                ForEach(data) { item in
                    
                    Button(action: {
                        // navigation action
                    }, label: {
                        RowView(item: item)
                    })
                    .opacity(showListItems ? 1 : 0)
                    .animation(Animation.easeOut(duration: 0.6).delay(animationDelay), value: showListItems)
                    .onAppear{
                        animationDelay = animationDelay + 0.2
                    }

                } //: ForEach
          
            } //: List

        } //: VStack
        .onAppear{
            showListItems = true
    }
}

2 Answers 2

12

The key seems to be using the indices from the ForEach loop to set the times at which the animations appear.

Below is the code. The toggle switch just resets the state to show the animation:

struct GuideListView: View {
    let data = ["One", "Two", "Three", "Four"]
    @State var showListItems = false
    @State var animationDelay = 0.5
    // definitions of viewRouter, data etc.
    
    var body: some View {
        
        VStack {
            
            // other items, navLink etc.
            Toggle("Show List Items", isOn: $showListItems)

            List {
                
                ForEach(data.indices) { index in
                    
                    Button(action: {
                        // navigation action
                    }, label: {
                        Text(data[index])
                    })
                        .opacity(showListItems ? 1 : 0)
                        .animation(Animation.easeOut(duration: 0.6).delay(animationDelay * Double(index)), value: showListItems)

                } //: ForEach
                
            } //: List
            
        } //: VStack
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

Thank you for your help. This is the right solution. I toyed around with it some more, because it did not work when triggered with onAppear{} rather than Toggle(). It turns out that a delay is needed in onAppear{} and everything will work correctly. What is more, introducing a delay made my original solution work as well! However, it shows all the rows animating at once, whereas your solution produces the desired behaviour where the rows animate one after another.
I missed the part in your original question where you said they weren't animating. I fixated on the all at once and solved that issue. I need to read more closely sometimes. Glad it is fixed!
Yes, it seems to be the solution. Unfortunately, since then I discovered that using indices really messed up filtering of the data, with Xcode throwing BoundsFails my way. But that is for another question. For now, I'm sticking with animating the entire list at once.
I will look forward to that question. I am not sure it is actually a problem. It may be an order of things issue.
0

You can also implement such approach. Of course, it is big problem if you want to use button inside List. But the following code can be useful. Use .buttonStyle(.plain) to highlight a button & .tag(index) to select only one button.

List {
        ForEach(coins.indices, id: \.self) { index in
            Button {
                // Add action ⚠️
                print("ACTION \(index)")
            } label: {
                ListRowButtonView(coin: coins[index])
            }
            .buttonStyle(.plain)
            .tag(index)

            ///List cell setting for `.listStyle(.plain)`
            .listRowBackground(Color.theme.background)
            .listRowSeparator(.hidden)
        }
    }
    .listStyle(.plain)

ListRowButtonView(coin: coins[index]) - Custom view outside existing code))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.