title: How to toggle animation in View from outside with preparation in SwiftUI?
body: <p>I'm building a UI component with SwiftUI that should have trigger from outside to turn on animation and some inner preparations for it. In examples below it's prepareArray() function.</p>
  
  <p>My first approach was to use bindings, but I've found that there is no way to listen when @Binding var changes to trigger something:</p>
  
  <pre><code>struct ParentView: View {
      @State private var animated: Bool = false
  
      var body: some View {
          VStack {
              TestView(animated: $animated)
              Spacer()
              Button(action: {
                  self.animated.toggle()
              }) {
                  Text("Toggle")
              }
              Spacer()
          }
      }
  }
  
  struct TestView: View {
      @State private var array = [Int]()
  
      @Binding var animated: Bool {
          didSet {
             prepareArray()
          }
      }
  
      var body: some View {
          Text("\(array.count): \(animated ? "Y" : "N")").background(animated ? Color.green : Color.red).animation(Animation.easeIn(duration: 0.5).delay(0.1))
      }
  
      private func prepareArray() {
          array = [1]
      }
  }
  </code></pre>
  
  <p>Why then it allows didSet listener for @Binding var if it's not working?! Then I switched to simple Combine signal since it's can be caught in onReceive closure. But @State on signal was not invalidating view on value pass:</p>
  
  <pre><code>struct ParentView: View {
      @State private var animatedSignal = CurrentValueSubject&lt;Bool, Never&gt;(false)
  
      var body: some View {
          VStack {
              TestView(animated: animatedSignal)
              Spacer()
              Button(action: {
                  self.animatedSignal.send(!self.animatedSignal.value)
              }) {
                  Text("Toggle")
              }
              Spacer()
          }
      }
  }
  
  struct TestView: View {
      @State private var array = [Int]()
  
      @State var animated: CurrentValueSubject&lt;Bool, Never&gt;
  
      var body: some View {
          Text("\(array.count): \(animated.value ? "Y" : "N")").background(animated.value ? Color.green : Color.red).animation(Animation.easeIn(duration: 0.5).delay(0.1)).onReceive(animated) { animated in
              if animated {
                  self.prepareArray()
              }
          }
      }
  
      private func prepareArray() {
          array = [1]
      }
  }
  </code></pre>
  
  <p>So my final approach was to trigger inner state var on signal value:</p>
  
  <pre><code>struct ParentView: View {
      @State private var animatedSignal = CurrentValueSubject&lt;Bool, Never&gt;(false)
  
      var body: some View {
          VStack {
              TestView(animated: animatedSignal)
              Spacer()
              Button(action: {
                  self.animatedSignal.send(!self.animatedSignal.value)
              }) {
                  Text("Toggle")
              }
              Spacer()
          }
      }
  }
  
  struct TestView: View {
      @State private var array = [Int]()
  
      let animated: CurrentValueSubject&lt;Bool, Never&gt;
      @State private var animatedInnerState: Bool = false {
          didSet {
              if animatedInnerState {
                  self.prepareArray()
              }
          }
      }
  
      var body: some View {
          Text("\(array.count): \(animatedInnerState ? "Y" : "N")").background(animatedInnerState ? Color.green : Color.red).animation(Animation.easeIn(duration: 0.5).delay(0.1)).onReceive(animated) { animated in
              self.animatedInnerState = animated
          }
      }
  
      private func prepareArray() {
          array = [1]
      }
  }
  </code></pre>
  
  <p>Which works fine, but I can't believe such a simple task requires so complicated construct! I know that SwiftUI is declarative, but may be I'm missing more simple approach for this task? Actually in real code this animated trigger will have to be passed to one more level deeper(</p>