2

Please see this sample view that demonstrates the problem:

struct ListRemovalTransition: View {
    let list1 = ["A", "B", "C", "D"]
    let list2 = ["A", "E", "F", "G", "H", "I", "J", "K"]

    @State var toggle = false
    var chosenList: [String] {
        toggle ? list1 : list2
    }

    var body: some View {
        VStack {
            Toggle(isOn: $toggle) {
                Text("Switch List")
            }

            List(chosenList, id: \.self) { item in
                Text(item)
                    .transition(AnyTransition.opacity.animation(.default))
            }
        }
        .padding()
    }
}

struct ListRemovalTransition_Previews: PreviewProvider {
    static var previews: some View {
        ListRemovalTransition()
    }
}

transition demo

The desired outcome is that the individual rows fade out when removed without changing position. Instead what's happening seems like all the rows first overlap each other before being removed. I've added a transition with animation to the row Text but this has no impact.

1 Answer 1

6

Just add id(:) modifier to List to remove default animation. Then add transition(:) modifier to List for your desirable transition. It works perfectly. I just tested on Xcode 11.5. Here is my code...

struct ListRemovalTransition: View {
    let list1 = ["A", "B", "C", "D"]
    let list2 = ["A", "E", "F", "G", "H", "I", "J", "K"]

    @State var toggle = false
    var chosenList: [String] {
        toggle ? list1 : list2
    }

    var body: some View {
        VStack {
            Toggle(isOn: $toggle) {
                Text("Switch List")
            }

            List(chosenList, id: \.self) { item in
                Text(item)
            }
            .id(UUID())
            .transition(AnyTransition.opacity.animation(.default))
        }
        .padding()
    }
}

demo

Thanks. X_X

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the answer! This solution definitely works, I'm curious if there's a way to do it without using .id(UUID()). While this example wouldn't benefit from it, it's more typical that the List has a single array as the data source so most elements would continue to be in the List after updates. If I understand .id correctly, it would ditch those items and init them again for the updated list which wouldn't be optimal.
.id(UUID()) is a unique identifier that makes List to rebuild entirely whenever state value is changed. So we cannot see animation. Without .id(UUID()), you will see default list transition because List does not rebuild and it just changes its data. Currently this is a proper way to remove default transition. I wish SwiftUI would provide to be customised as we want.

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.