Animation on Toggle Change with SwiftUI
Asked Answered
H

1

8

I am building a simple settings screen.

When then first setting is activated, the Speed Control appears. When it's turned off, the Speed Control disappears.

Based on what I know about SwiftUI, this should automatically animate based on the code below, but instead it just appears and disappears.

How can I make this animation nicer with SwiftUI, so it slides down from the cell above it, like in this presentation at 53:00?

GIF of setting animation

import SwiftUI
import Combine

struct ContentView : View {

    @ObjectBinding var settingsStore: SettingsStore

    var body: some View {
        NavigationView {
            Form {
                Toggle(isOn: $settingsStore.settingActivated.animation(.basic(duration: 3, curve: .easeInOut))) {
                    Text("Setting Activated")
                }
                if settingsStore.settingActivated {
                    VStack(alignment: .leading) {
                        Text("Speed Control")
                        HStack {
                            Image(systemName: "tortoise")
                            Slider(value: .constant(10), from: 0, through: 50, by: 1)
                            Image(systemName: "hare")
                        }
                    }.transition(.opacity)
                }
            }.navigationBarTitle(Text("Settings"))
        }
    }
}

class SettingsStore: BindableObject {

    let didChange = PassthroughSubject<Void, Never>()

    var settingActivated: Bool = UserDefaults.settingActivated {
        didSet {

            UserDefaults.settingActivated = settingActivated

            didChange.send()
        }
    }
}

extension UserDefaults {

    private struct Keys {
        static let settingActivated = "SettingActivated"
    }

    static var settingActivated: Bool {
        get {
            return UserDefaults.standard.bool(forKey: Keys.settingActivated)
        }
        set {
            UserDefaults.standard.set(newValue, forKey: Keys.settingActivated)
        }
    }
}
Harpole answered 8/7, 2019 at 0:50 Comment(3)
It must be a bug. If you remove the embedding NavigationView and replace Form with Stack, you will see that animations start to work. The only reason I can think for that behavior, is a bug. You should file one with Apple.Romelda
It is a bug - a known issue (although I don't have a reference for it) that when NavigationView is enabled within the view, the animation doesn't work as you'd expect.Arel
The issue still persists on Xcode 14.1 using NavigationStackInvective
E
2

The same bug is still present in iOS 15 and iOS 16. You now can use the property @AppStorage which takes care of reading and writing the value in the default store, but animations still don't work with that.

struct SettingsView: View {
    @AppStorage("SettingActivated") var settingActivated: Bool = false
    var body: some View {
        Form {
            Toggle(isOn: $settingActivated.animation(.basic(duration: 3, curve: .easeInOut))) {
                Text("Activate Setting")
            }
            if settingActivated {
                Text("Setting Activated")
            }
        }
    }
}

I found a workaround which requires little extra code and then works fine. You need the usual @State property for SwiftUI to run animations, and you can just set this whenever the @AppStorage property changes (1):

struct SettingsView: View {
    @AppStorage("SettingActivated") var settingActivated: Bool = false
    @State private var settingShown: Bool = false
    var body: some View {
        Form {
            Toggle("Activate Setting", isOn: $settingActivated.animation())
                .onChange(of: value) { value in
                    withAnimation {settingShown = value}  // 1)
                }
            if settingShown {
                Text("Setting Activated")
            }
        }
        .onAppear() {
            settingShown = settingActivated  // 2)
        }
    }
}

However, since you cannot initialize the @State from the @AppStorage, you need to do this in .onAppear. (2)

And that's it. Animations in Forms and Lists, triggered by @AppStorage toggle.

Equine answered 29/5, 2023 at 10:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.