skip to Main Content

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:

enter image description here

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 {
                    ProgressView()
                }
                else {
                    EmptyView()
                }
            }
            .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.

2

Answers


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

    The result
    The result

    Login or Signup to reply.
  2. 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()
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search