Search code examples
swiftswiftuiswiftui-animationswiftui-button

SwiftUI Animation onLongPressGesture(minimumDuration: 0.5) automatically starts


I've created a circular button which I specified to show scale animation on .onLongPressGesture(minimumDuration: 0.5) the code is bellow:

struct PlayerTouch: View {
   @State private var animationAmount: CGFloat = 1
   @State var animationEffect   = false
   
   var outerColor: Color
   var innerColor: Color

   let touchpulse = Animation.linear.repeatForever(autoreverses: true)

   private var outerBorderCirle: some View {
    let side:  CGFloat = Device.model == device.Six_6S_7_8_SE ? 108.6 : 146
 
    return Circle()
        .stroke(outerColor, lineWidth: 6)
        .frame(width: side, height: side)
        .shadow(color: Color.black.opacity(0.3) ,radius: 8 , x: 0, y: 0)
        
    
}
   private var innerBorderCirle: some View {
    let side:  CGFloat = Device.model == device.Six_6S_7_8_SE ? 80.3 : 108
   
    return Circle()
        .stroke(innerColor, lineWidth: 20)
        .frame(width: side, height: side)
        .scaleEffect(animationEffect ? 0.9 : 1)
        .opacity(animationEffect ? 0.7 : 1)
        .animation(touchpulse)
                   

}
   private var touchCirle:       some View {
    let side:  CGFloat = Device.model == device.Six_6S_7_8_SE ? 71.7 : 96
    
    return Circle()
        .foregroundColor(colors.darkCream.value)
        .shadow(color: colors.touchCream.value, radius: 18, x: 0, y: 6)
        .frame(width: side, height: side)
        .onLongPressGesture(minimumDuration: 0.5) {
            self.animationEffect = true
        }
}
   }



     var body: some View {
       ZStack{
        outerBorderCirle
        innerBorderCirle
        touchCirle
       }
   }}

and I've created 4 instances from this view in my main view like bellow:

struct GameScene: View {
    @Binding var numberOfPeople: Int
    @State var offset = 0

    private var touch1: some View {
        PlayerTouch(outerColor: outerColors[0], innerColor: innerColors[0])
    }
    private var touch2: some View {
        PlayerTouch(outerColor: outerColors[1], innerColor: innerColors[1])
    }
    private var touch3: some View {
        PlayerTouch(outerColor: outerColors[2], innerColor: innerColors[2])
    }
    private var touch4: some View {
        PlayerTouch(outerColor: outerColors[3], innerColor: innerColors[3])
    }

    var body: some View {
   VStack(spacing: Device.model! == .X_Xs_11Pro ? 20 : 40){
    
        HStack{
            Spacer()
            touch1
                
            touch2
                .offset(y: Device.model! == .Six_6S_7_8_SE &&
                numberOfPeople > 2 ? -40 : -50)
            Spacer()
        }
        HStack{
    
            if numberOfPeople > 2{
                Spacer()
                touch3
                    .offset(x: numberOfPeople == 3 ? -40 : 0, y: numberOfPeople == 3 ? -40 : 0)
            }
            if numberOfPeople > 3 {
                touch4
                .offset(y:-50)
                Spacer()
            }
    
        }
    
        }.frame(height: height)
        .padding(.leading, 10)
        .padding(.trailing, 10)
}}

and I have a button to add numberofpeople which leads to show instances on the view base on that. my problem is animation automatically triggers after adding to numberofpeople here is the gif of showing the problem:

The animation problem shown

the right way of showing animation is after I touching it for 0.5 seconds

Animation should be like this and should be triggered after long tap


Solution

  • Make animation active explicit per-value change, like

    return Circle()
        .stroke(innerColor, lineWidth: 20)
        .frame(width: side, height: side)
        .scaleEffect(animationEffect ? 0.9 : 1)
        .opacity(animationEffect ? 0.7 : 1)
        .animation(touchpulse, value: animationEffect)       // << here !!