How to add transition for conditionally rendered view in SwiftUI?

Viewed 472

How to add a transition for a conditionally rendered view inside a GroupedListStyle list? to achieve something like the conditional DatePicker from the "Do Not Disturb" section in Settings (see GIF below).

Adding transition and animation for the conditionally rendered view does not seem to work.

if condition {
    Toggle(isOn: $foo) {
        HStack {
            Text("Foo")
        }
    }
    .transition(.opacity)
    .animation(.easeInOut)
    .toggleStyle(SwitchToggleStyle(tint: Color.red)
}

enter image description here

Implementation based on pawello2222

The animation is that smooth especially when the view about to disappear. The transition position was animated from frame 1 to 11 but suddenly disappeared from frame 12 onwards.

enter image description here

enter image description here

2 Answers

Here is a possible solution, changing the Published value with the help of withAnimation

class ContentViewModel : ObservableObject {
    @Published var isOn : Bool = false {
        didSet {
            withAnimation {
                if isOn {
                    showRow = true
                }
                else {
                    showRow = false
                }
            }
        }
    }
    
    @Published var showRow = false
}

struct ContentView: View {
    
    @ObservedObject var viewModel = ContentViewModel()
            
    var body: some View {
        
        List() {
            
            Text("Hello World")

            HStack() {
                Toggle("Test", isOn: $viewModel.isOn)
                    .toggleStyle(SwitchToggleStyle(tint: Color.red))
            }
            
            if (viewModel.showRow) {
                Text("Hello Second World")
            }
       }.listStyle(GroupedListStyle())
    }
}

It might be better to keep the animation-related logic (like withAnimation) inside the view.

A possible solution is to create a custom Binding:

struct ContentView: View {
    @StateObject var viewModel = ViewModel()

    var binding: Binding<Bool> {
        .init(get: {
            viewModel.showRow
        }, set: { newValue in
            withAnimation {
                viewModel.showRow = newValue
            }
        })
    }

    var body: some View {
        List {
            Text("Text")
            Toggle("Toggle", isOn: binding)
                .toggleStyle(SwitchToggleStyle(tint: Color.red))
            if viewModel.showRow {
                Text("Hidden Text")
            }
        }
        .listStyle(GroupedListStyle())
    }
}
Related