Home > Net >  How can I be able animate value change in Shape?
How can I be able animate value change in Shape?

Time:12-22

I am updating my shape with a variable called count which is a Int, I implemented the needed code for applying the animation for Shape even explicitly used the transition, but i am not able to animate my shape, looking to solve and learn the issue in my code.

Xcode Version 14.1 (14B47b) in SwiftUI-macOS

 struct ContentView: View {
    
    @State private var count: Int = 1
    var body: some View {
        VStack(spacing: 0.0) {

            RectangleShape(count: count)
                .fill()
            
            Button("update") {
                if (count >= 4) { count = 1 }
                else { count  = 1 }
            }
            .padding()
            
        }
        .padding(.horizontal)
        .padding(.top)
        .transition(AnyTransition.opacity)
        .animation(.default, value: count)
 
    }
}



struct RectangleShape: Shape {
    
    var count: Int

    var animatableData: Int {
        get { count }
        set { count = newValue }
    }
    
    func path(in rect: CGRect) -> Path {

        return Path { path in

            switch count {
            case 2:
                path.move(to: CGPoint(x: rect.minX, y: rect.minY))
                path.addRect(CGRect(x: rect.minX, y: rect.minY, width: rect.width/2.0, height: rect.height/2.0))
                path.addRect(CGRect(x: rect.midX, y: rect.minY, width: rect.width/2.0, height: rect.height/2.0))
            case 3:
                path.move(to: CGPoint(x: rect.minX, y: rect.minY))
                path.addRect(CGRect(x: rect.minX, y: rect.minY, width: rect.width/2.0, height: rect.height/2.0))
                path.addRect(CGRect(x: rect.midX, y: rect.minY, width: rect.width/2.0, height: rect.height/2.0))
                path.addRect(CGRect(x: rect.midX, y: rect.midY, width: rect.width/2.0, height: rect.height/2.0))
            case 4:
                path.move(to: CGPoint(x: rect.minX, y: rect.minY))
                path.addRect(CGRect(x: rect.minX, y: rect.minY, width: rect.width/2.0, height: rect.height/2.0))
                path.addRect(CGRect(x: rect.midX, y: rect.midY, width: rect.width/2.0, height: rect.height/2.0))
                path.addRect(CGRect(x: rect.minX, y: rect.midY, width: rect.width/2.0, height: rect.height/2.0))
                path.addRect(CGRect(x: rect.midX, y: rect.minY, width: rect.width/2.0, height: rect.height/2.0))
            default:
                path.move(to: CGPoint(x: rect.minX, y: rect.minY))
                path.addRect(CGRect(x: rect.minX, y: rect.minY, width: rect.width/2.0, height: rect.height/2.0))
            }

        }
    }
        
}

CodePudding user response:

Your approach is going into the right direction, just some aspects:

The animatableData var count should be Double (not Int) as SwiftUI can only animate by changing from the start value to the end value in incremental steps, which it couldn't do with Int.

Based on this incremental change of count SwiftUI is calling your Shape over and over again. So your shape has to be able to draw the "in between" steps by reacting on count. I did include code for that based on my understanding of what you might want to achieve.

Lastly I commented out things you won't need.

enter image description here

struct ContentView: View {
    
    @State private var count: Double = 0 // use Double, start with 0
    var body: some View {
        VStack(spacing: 0.0) {

            RectangleShape(count: count)
                .fill()
            
            Button("update") {
                    if (count >= 4) { count = 0 } // reset to 0
                    else { count  = 1 }
            }
            .padding()
            
        }
        .padding(.horizontal)
        .padding(.top)
//        .transition(AnyTransition.opacity)
        .animation(.default, value: count)
 
    }
}



struct RectangleShape: Shape {
    
    var count: Double // use Double

    var animatableData: Double {
        get { count }
        set { count = newValue }
    }
    
    func path(in rect: CGRect) -> Path {

        return Path { path in

            switch count {
            case 0...1:
                path.addRect(CGRect(x: rect.minX, y: rect.minY,
                                    width: rect.width/2.0 * count, height: rect.height/2.0))
            case 1...2:
//                path.move(to: CGPoint(x: rect.minX, y: rect.minY))
                path.addRect(CGRect(x: rect.minX, y: rect.minY,
                                    width: rect.width/2.0, height: rect.height/2.0))
                path.addRect(CGRect(x: rect.midX, y: rect.minY,
                                    width: rect.width/2.0 * (count-1), height: rect.height/2.0 ))
            case 2...3:
//                path.move(to: CGPoint(x: rect.minX, y: rect.minY))
                path.addRect(CGRect(x: rect.minX, y: rect.minY,
                                    width: rect.width/2.0, height: rect.height/2.0))
                path.addRect(CGRect(x: rect.midX, y: rect.minY,
                                    width: rect.width/2.0, height: rect.height/2.0))
                path.addRect(CGRect(x: rect.midX, y: rect.midY,
                                    width: rect.width/2.0, height: rect.height/2.0 * (count-2)))
            case 3...4:
//                path.move(to: CGPoint(x: rect.minX, y: rect.minY))
                path.addRect(CGRect(x: rect.minX, y: rect.minY,
                                    width: rect.width/2.0, height: rect.height/2.0))
                path.addRect(CGRect(x: rect.midX, y: rect.minY,
                                    width: rect.width/2.0, height: rect.height/2.0))
                path.addRect(CGRect(x: rect.midX, y: rect.midY,
                                    width: rect.width/2.0, height: rect.height/2.0))
                path.addRect(CGRect(x: rect.midX - rect.width/2.0 * (count-3), y: rect.midY,
                                    width: rect.width/2.0 * (count-3), height: rect.height/2.0))
            default: do {}
//                path.move(to: CGPoint(x: rect.minX, y: rect.minY))
            }

        }
    }
        
}
  • Related