loop animation Image scale & opacity SwiftUI

139 Views Asked by At

I have button, with 3 states. none, recording and result. When I start recording, I want to show animation in loop, but when I change it to result state or none state - I should stop animation. reason why I didn't implemented with ternary operator cause I need to know when user end recording, to show him result view.

So,my question: How to make animation with image (change scale and opacity), without method .onAppear{} ? Cause when I change state to result, and then back again to recording, .onAppear{} will not work.

I need to show animation, every time, when I change state to recording.

My code below:

enum RecordState {
    case recording
    case notRecording
    case none
}
VStack {
                    Button {
                        switch recordState {
                        case .recording:
                            recordState = .notRecording
                        case .notRecording:
                            recordState = .none
                        case .none:
                            recordState = .recording
                        }
                        
                    } label: {
                        switch recordState {
                        case .recording:
                            ZStack {
                                Spacer()
                                Image("ic_record_cancel")
                                    .resizable()
                                    .frame(width: 76, height: 76)
                                    .zIndex(1.0)
                                
                                Image("ic_record_effect")
                                    .resizable()
                                    .frame(width: 76, height: 76)
                                    .zIndex(0.1)
                                    .opacity(opacity)
                                    .scaleEffect(scale)
                                    .onAppear {
                         let baseAnimation = Animation.linear(duration: 0.8).delay(0.2)
                         let repeated = baseAnimation.repeatForever(autoreverses: true)
                                    withAnimation(repeated) {
                                     scale = 1.0
                                     opacity = 0.6
        }
                                    }
                            }
                        case .notRecording:
                                Image("ic_record_start")
                                    .resizable()
                                    .frame(width: 76, height: 76)
                        case .none:
                                Image("ic_record_start")
                                    .resizable()
                                    .frame(width: 76, height: 76)
                        }
                        
                    }
                }

now, after a change from recording to result, my image where i have animation .onDisappear{}.

1

There are 1 best solutions below

0
Benzy Neez On

The reason why .onAppear is not working the second time is maybe because you are not resetting the state variables back to their initial values (whatever they were) when the recording stops. One way to do this would be to add an .onDisappear to the image "ic_record_effect":

    Image("ic_record_effect")
        // other modifiers as before
        .onAppear {
            // also as before
        }
        .onDisappear {
            scale = 2
            opacity = 1
        }

Alternatively, you could remove the .onAppear modifier from the image and add an .onChange modifier to the surrounding VStack instead. Here too, you would need to reset the values when recording stops:

    VStack {
        // contents as before, without .onAppear
    }
    .onChange(of: recordState) { newState in
        if newState == .recording {
            let baseAnimation = Animation.linear(duration: 0.8).delay(0.2)
            let repeated = baseAnimation.repeatForever(autoreverses: true)
            withAnimation(repeated) {
                scale = 1.0
                opacity = 0.6
            }
        } else {
            scale = 2
            opacity = 1
        }
    }