I have a custom bounce animation applied to a view with multiple inner views which can change depending on conditions. My issue is that when the inner view changes, the animation applied to a parent no longer applies to it.
Example:
Sample Code:
struct ContentView: View {
@State var number = 1
var body: some View {
VStack {
ZStack {
Circle()
.foregroundColor(.gray)
.frame(width: 100, height: 100, alignment: .center)
if number == 1 {
Image(systemName: "person")
}
else if number == 2 {
Image(systemName: "globe")
}
else {
Image(systemName: "square")
}
}
.bounceEffect()
Button {
if number != 3 {
number = 1
}
else {
number = 1
}
} label: {
Text("CHANGE")
}
.padding()
}
.padding()
}
}
struct BounceEffect: ViewModifier {
@State var bounce = false
var allow: Bool
func body(content: Content) -> some View {
content
.offset(y: (bounce && allow) ? -5 : 0)
.animation(.interpolatingSpring(mass: 1, stiffness: 350, damping: 5, initialVelocity: 10).repeatForever(autoreverses: false).delay(1), value: UUID())
.onAppear {
if allow {
bounce.toggle()
}
}
}
}
extension View {
func bounceEffect(allow: Bool = true) -> some View {
modifier(BounceEffect(allow: allow))
}
}
Thank you.
CodePudding user response:
The reason of the bounceEffect don't get the button after you change because you are creating the new image when ever you tap the button change
.
The action you need here: just only change the system image of the image not creating new one every time the button change
is tapped
The code will be like this
struct ContentView: View {
@State var number = 1
@State var imageName = "person"
var body: some View {
VStack {
ZStack {
Circle()
.foregroundColor(.gray)
.frame(width: 100, height: 100, alignment: .center)
Image(systemName: imageName)
}
.bounceEffect()
Button {
if number != 3 {
number = 1
}
else {
number = 1
}
if number == 1 {
imageName = "person"
}
else if number == 2 {
imageName = "globe"
}
else {
imageName = "square"
}
} label: {
Text("CHANGE")
}
.padding()
}
.padding()
}
}
CodePudding user response:
When the new images appear, they aren’t the ones involved in the animation. One way to fix this is to include all 3 images from the start, and just cycle their visibility by changing opacity
:
ZStack {
Circle()
.foregroundColor(.gray)
.frame(width: 100, height: 100, alignment: .center)
Image(systemName: "person")
.opacity(number == 1 ? 1 : 0)
Image(systemName: "globe")
.opacity(number == 2 ? 1 : 0)
Image(systemName: "square")
.opacity(number == 3 ? 1 : 0)
}
@bewithyou’s answer is more scalable and should be the accepted answer. Here is a better way to structure the selection of your images:
struct ContentView: View {
@State var number = 0
let names = ["person", "globe", "square", "house", "car"]
var body: some View {
VStack {
ZStack {
Circle()
.foregroundColor(.gray)
.frame(width: 100, height: 100, alignment: .center)
Image(systemName: names[number])
}
.bounceEffect()
Button {
number = (number 1) % names.count
} label: {
Text("CHANGE")
}
.padding()
}
.padding()
}
}