SwiftUI ReadMore chevron animation guidance appreciated

Hello,

I'm attempting to create a basic showMore/showLess control at the bottom of a large block of text. However it is currently animating in a suboptimal way. When I tap to expand my text block:

  1. the text block expands right away
  2. the text label changes from 'More' to 'Less' right away
  3. the chevron animates both the rotationEffect and its change in position

The problem is animating the change of position is odd as it is moving 'over' the expanded text block.

My fallback position is to remove the rotationEffect and have the Image(systemName:) choose either "chevron.up" or "chevron.down" depending on is expanded. But is there a way to either:

  1. animate the rotationEffect, but have the position change instantly -or-
  2. have the more/less label and the chevron both animate the position change together?

thanks in advance for any help

struct ContentView: View {
  @State private var isExpanded = false

  let text = "That you have wronged me doth appear in this: You have condemned and noted Lucius Pella For taking bribes here of the Sardians, wherein my letters, praying on his side because I knew the man, were slighted off."

  var body: some View {
    VStack(alignment: .leading) {
      Text(text)
        .frame(maxWidth: 300)
        .lineLimit(isExpanded ? nil : 2)
      footer
    }
    .padding()
  }

  @ViewBuilder
  private var footer: some View {
    HStack {
      Text(isExpanded ? "Less" : "More")
      Button {
        isExpanded.toggle()
      } label: {
        Image(systemName: "chevron.up")
          .rotationEffect(isExpanded ? .degrees(180) : .zero)
      }

    }
  }
}

I've tried this, which seems closer to what you look for:

struct ContentView: View {
    @State private var isExpanded = false
    @State private var changing = false

    let text = "That you have wronged me doth appear in this: You have condemned and noted Lucius Pella For taking bribes here of the Sardians, wherein my letters, praying on his side because I knew the man, were slighted off."
    
    var body: some View {
        VStack(alignment: .leading) {
            Text(text)
                .frame(maxWidth: 300)
                .lineLimit(isExpanded ? nil : 2)
            footer
                .isHidden(changing)
        }
        .padding()
    }
    
    @ViewBuilder
    private var footer: some View {
        HStack {
            Text(isExpanded ? "Less" : "More")
            Button {
                changing = true // We change the chevron…
                isExpanded.toggle()
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
                    changing = false    // Change is done
                }
                
            } label: {
                Image(systemName: "chevron.up")
                    .rotationEffect(isExpanded ? .degrees(180) : .zero)
            }
            
        }
    }
}

You could replace rotation effect by:

               Image(systemName: isExpanded ? "chevron.up" : "chevron.down")

I did this to get the appropriate animation effect on the chevron

struct ContentView: View {
    @State private var isExpanded = false
    @State private var changing = false
    @State private var rotation = 0.0

    let text = "That you have wronged me doth appear in this: You have condemned and noted Lucius Pella For taking bribes here of the Sardians, wherein my letters, praying on his side because I knew the man, were slighted off."
    
    var body: some View {
        VStack(alignment: .leading) {
            Text(text)
                .frame(maxWidth: 300)
                .lineLimit(isExpanded ? nil : 2)
            footer
                .isHidden(changing)
                .onChange(of: changing, perform: { _ in rotation += changing ? 0 : 180 } ) 
        }
        .padding()
    }
    
    @ViewBuilder
    private var footer: some View {
        HStack {
            Text(isExpanded ? "Less" : "More")
            Button {
                changing = true // We change the chevron…
                isExpanded.toggle()
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { // enough to update text
                    changing = false    // Change is done
                }
                
            } label: {
                Image(systemName: "chevron.up")
                    .rotationEffect(.degrees(rotation))
                    .animation(.easeInOut(duration: 1.0), value: rotation)
            }
            
        }
    }
}
SwiftUI ReadMore chevron animation guidance appreciated
 
 
Q