SwiftUI | Anima questa forma del percorso

Oct 20 2020

Ho il seguente caso:

Voglio capovolgere questa forma con un'animazione personalizzata. Non so come descriverlo correttamente.

Ogni volta che tocco la freccia, dovrebbe trasformarsi nell'altra. Senza capovolgere, ruotare, ecc. Semplicemente trasformarsi.

Se non è abbastanza preciso, sentiti libero di commentare!

Codice demo

struct ContentView: View {
    
    @State private var change = false
    
    private let arrowWidth: CGFloat = 80
    
    
    var body: some View {
        Path { path in
            path.move(to: .zero)
            path.addLine(to: CGPoint(x: arrowWidth/2, y: change ? -20 : 20))
            path.move(to: CGPoint(x: arrowWidth/2, y: change ? -20 : 20))
            path.addLine(to: CGPoint(x: arrowWidth, y: 0))
        }
        .stroke(style: StrokeStyle(lineWidth: 12, lineCap: .round))
        .frame(width: arrowWidth)
        .foregroundColor(.green)
        .animation(.default)
        .onTapGesture { change.toggle() }
        .padding(.top, 300)
    }
}

Come puoi vedere ora non ha animazione. Non ho idea di come farlo.

Ogni aiuto è molto apprezzato. Grazie!

Nota: non posso farlo con due rettangoli arrotondati + semplicemente ruotando, poiché ho un'animazione di opacità, che rende visibile la sovrapposizione.

Risposte

7 Asperi Oct 20 2020 at 20:45

Ecco un possibile approccio: sposta il percorso nella forma personalizzata e imposta il parametro modificato come proprietà animabile. Testato con Xcode 12 / iOS 14

struct MyArrow: Shape {
    var width: CGFloat
    var offset: CGFloat
    
    var animatableData: CGFloat {
        get { offset }
        set { offset = newValue }
    }
    
    func path(in rect: CGRect) -> Path {
        Path { path in
            path.move(to: .zero)
            path.addLine(to: CGPoint(x: width/2, y: offset))
            path.move(to: CGPoint(x: width/2, y: offset))
            path.addLine(to: CGPoint(x: width, y: 0))
        }
    }
}

struct ContentView: View {
    
    @State private var change = false
    
    private let arrowWidth: CGFloat = 80
    
    
    var body: some View {
        MyArrow(width: arrowWidth, offset: change ? -20 : 20)
        .stroke(style: StrokeStyle(lineWidth: 12, lineCap: .round))
        .frame(width: arrowWidth)
        .foregroundColor(.green)
        .animation(.default)
        .onTapGesture { change.toggle() }
        .padding(.top, 300)
    }
}