SwiftUI | Animer cette forme de chemin

Oct 20 2020

J'ai le cas suivant:

Je veux inverser cette forme avec une animation personnalisée. Je ne sais pas comment le décrire correctement.

Chaque fois que je tape sur la flèche, elle doit se transformer en une autre. Sans retourner, tourner, etc., simplement transformer.

Si ce n'est pas assez précis, n'hésitez pas à commenter!

Code démo

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)
    }
}

Comme vous pouvez le voir maintenant, il n'a pas d'animation. Je ne sais pas comment faire.

Toute aide est très appréciée. Merci!

Remarque: je ne peux pas faire cela avec deux rectangles arrondis + simplement en rotation, car j'ai une animation d'opacité, ce qui rend le chevauchement visible.

Réponses

7 Asperi Oct 20 2020 at 20:45

Voici une approche possible - déplacez le chemin dans une forme personnalisée et faites du paramètre modifié comme propriété animable. Testé avec 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)
    }
}