SwiftUI ButtonStyle animation causes UI not to update in App Switcher snapshot when system appearance changes

1.3k views Asked by At

In my app I have a button that I want to animate to scale down when it's touched. To do this, I created a custom ButtonStyle with an explicit animation. This works as intended but it introduced a problem when switching between dark/light mode system appearances. If you change it while the app is open, the Color updates as expected, but if the app is not open, the snapshot in the App Switcher shows the wrong color for the new system appearance. The following code should be a black box on top of a white background in light mode and white on black in dark mode. The background always properly updates, but the box remains the old color in the snapshot and thus you cannot see it - black on black or white on white - at least as of iOS 14.2. The issue is the animation - if I remove that the snapshot appears as expected. Why is that and how can I fix it?

struct ContentView: View {
    var body: some View {
        Button(action: {}) {
            Color.primary
                .frame(height: 100)
        }
        .buttonStyle(MyButtonStyle())
        .padding()
    }
}

struct MyButtonStyle: ButtonStyle {
    func makeBody(configuration: Self.Configuration) -> some View {
        configuration.label
            .scaleEffect(configuration.isPressed ? 0.8 : 1)
            .animation(.easeOut(duration: 0.5)) //FIXME: Removing this fixes it
    }
}

Here I am running the app in light mode, then I closed it, enabled dark mode, then opened App Switcher to find the box is black on the black background. The box should be white, as it is when I tap the app to return it to the foreground.

Screenshots

1

There are 1 answers

0
Asperi On BEST ANSWER

Works fine with iOS 14.1, so I assume it is another 14.2 defect.

Try to link animation to value (not tested, just idea):

struct MyButtonStyle: ButtonStyle {
    func makeBody(configuration: Self.Configuration) -> some View {
        configuration.label
            .scaleEffect(configuration.isPressed ? 0.8 : 1)
            .animation(.easeOut(duration: 0.5), value: configuration.isPressed)
    }
}