How Can WithAnimation Code be placed inside the view to be animated

Hi. My view gets animated with a call frmo a block of code withAnimation.{ }

how can i place this inside the view so that the binding variable will be the one to trigger the animation. Same as how sheets work.

THis is my sample view

import SwiftUI

struct GradientLegendView: View {
   
  @Binding var isShowingLegend: Bool
   
  var labels: [String]?
  var colors: [Color]?
  var width: Float
  var height: Float
  var viewRect = Rectangle()
   
  var body: some View {
    if isShowingLegend {
      HStack {
        VStack {
          if let labels {
            ForEach(labels, id:\.self) { label in
              Text(label)
                .frame(maxWidth: .infinity, alignment: .leading)
                .font(.system(size: 14))
                .foregroundColor(.black)
                .backgroundStyle(.red)
                .padding(0)
               
              if label != labels.last {
                Spacer()
              }
            }
          }
        }
        .frame(maxHeight: .infinity)
        .padding(8)
        Rectangle()
          .foregroundColor(.clear)
          .background(LinearGradient(gradient: Gradient(colors: colors ?? []), startPoint: .top, endPoint: .bottom))
          .padding(8)
          .frame(width: 30)
      }
      .frame(width: CGFloat(width), height: CGFloat(height))
      .background(.white.opacity(0.7))
      .cornerRadius(5)
      .shadow(color: Color.gray.opacity(0.7), radius: 8, x: 0, y: 0)
      .padding()
      .transition(.move(edge: isShowingLegend ? .leading : .trailing))
    }
  }
}

struct GradientLegendView_Previews: PreviewProvider {
   
  static var previews: some View {
    GradientLegendView(
      isShowingLegend: .constant(true),
      labels: SampleData.createHeatmapLegendLabelArray(),
      colors: SampleData.createHeatmapLegendColorArray(),
      width: 120,
      height: 200
    )
  }
}

struct Sample {
static func createHeatmapLegendLabelArray() -> [String] {
    return ["Great", "Major", "Strong", "Moderate", "Small"]
  }
   
   func createHeatmapLegendColorArray() -> [Color] {
    return [.red, .purple, .orange, .green]
  }
}

And i use it here and run the animation using withAnimation { }

@State private var isShowingLegend = true

var body: some View {
    Text("Hey")
     .frame(maxWidth: .infinity, maxHeight: .infinity)
    .edgesIgnoringSafeArea(.all)
    .task {
      setupMenuItems()
    }
    .overlay(
      HStack {
          GradientLegendView(
            isShowingLegend: $isShowingLegend,
            labels: SampleData.createHeatmapLegendLabelArray(),
            colors: SampleData.createHeatmapLegendColorArray(),
            width: 120,
            height: 200)
        Spacer()
      },
      alignment: .bottom
    )
}

Now, say i also have a button instead of Text("hey") where the action code is

withAnimation(isShowingLegend ? .easeIn(duration: 0.5) : .easeOut(duration: 0.5)) {
        isShowingLegend.toggle()
      }

Is it possible that this withAnimatoin be placed inslide GradientLegendView and will execute if the isShowingLegend value is change?

Your code wouldn't compile as-is, but I think you want your GradientLegendView to slide in from the leading edge, and out on the trailing edge of the ContentView, in response to pressing a button. And you don't want to animate the isShowingLegend variable, you'd rather attach the animation to your GradientLegendView.

The thing is, what you want to animate (the position of the GradientLegendView) isn't intrinsic to the GradientLegendView. The view position is a property of the GradientLegendView which only makes sense in the ContentView.

I took the .transition modifier out of the GradientLegendView, and removed its isShowingLegend. The GradientLegendView is the thing which shows the gradient legend; it doesn't make sense for it to take that bool parameter. The piece of UI which uses the GradientLegendView decides whether to show it or not.

I attached a .transition(.slide) to the HStack which builds your GradientLegendView, and provided a .animation controlled by isShowingLegend. The animation is applied to a Group in your overlay. The Group returns an HStack if isShowingLegend is true and an (implied) EmptyView otherwise. It is necessary because the type of the parameter to .overlay should be some View.

this is my ContentView


    @State private var isShowingLegend = true

    var body: some View {
        Button("Press Me") {
            isShowingLegend.toggle()
        }
         .frame(maxWidth: .infinity, maxHeight: .infinity)
        .edgesIgnoringSafeArea(.all)
//        .task {
//          setupMenuItems() // won't compile, don't have
//        }
        .overlay(
            Group {
                if isShowingLegend {
                    HStack {
                        GradientLegendView(
                            // doesn't need isShowingLegend
                            labels: SampleData.createHeatmapLegendLabelArray(),
                            colors: SampleData.createHeatmapLegendColorArray(),
                            width: 120,
                            height: 200)
                        Spacer()
                    }
                    .transition(.slide)
                }
            }
            .animation(.easeInOut, value:isShowingLegend)
          ,
          alignment: .bottom
        )
    }
}

I don't know if this is what you are looking for (or were looking for, because it has been two weeks!), but I hope it helps.

This is a pretty good article about transitions https://www.objc.io/blog/2022/04/14/transitions/ And I found this tutorial very useful also https://www.hackingwithswift.com/books/ios-swiftui/creating-implicit-animations

How Can WithAnimation Code be placed inside the view to be animated
 
 
Q