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