0

using the code below as an example

let shootUp = AnyTransition.opacity.animation(.easeIn(duration: 5))

opacity works & can be delayed by changing the duration

    let shootUp = AnyTransition.offset(x: 0, y: -1000).animation(.easeIn(duration: 5))

offset fails, & cant be delayed by changing the duration

struct SpinnerView: View {
    struct Leaf: View {
        let rotation: Angle
        let isCurrent: Bool
        let isCompleting: Bool
        
        var body: some View {
            Capsule()
                .stroke(isCurrent ? Color.white : .gray, lineWidth: 8)
                .frame(width: 20, height: isCompleting ? 20 : 50)
                .offset(
                    isCurrent
                    ? .init(width: 10, height: 0)
                    : .init(width: 40, height: 70)
                )
                .scaleEffect(isCurrent ? 0.5 : 1)
                .rotationEffect(isCompleting ? .zero : rotation)
                .animation(.easeIn(duration: 1.5), value: isCompleting)
                .animation(.easeIn(duration: 1.5), value: isCurrent)
        }
    }
    
    @State var currentIndex = -1
    @State var completed = false
    @State var isVisible = true
    

>     let shootUp = AnyTransition.offset(x: 0, y: -1000).animation(.easeIn(duration: 5))

    
//    let shootUp = AnyTransition.opacity.animation(.easeIn(duration: 5))
    
    var body: some View {
        VStack {
            if isVisible {
                ZStack {
                    ForEach(0..<12) { index in
                        Leaf(
                            rotation: .init(degrees: .init(index) / .init(12) * 360),
                            isCurrent: index == currentIndex,
                            isCompleting: completed
                        )
                    }
                }
                .transition(shootUp)
                .onAppear(perform: animate)
            }
        }
    }
    
    func animate() {
        var iteration = 0
        
        Timer.scheduledTimer(withTimeInterval: 0.15, repeats: true) { timer in
            currentIndex = (currentIndex + 1) % 12
            
            iteration += 1
            if iteration == 30 {
                timer.invalidate()
                completed = true
                currentIndex = -1
                DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) {
                    withAnimation {
                        isVisible = false
                    }
                }
            }
        }
    }
}
byaruhaf
  • 4,128
  • 2
  • 32
  • 50
  • when I run your code `.offset` works, and `.opacity` does not. --- which is not surprising with a duration of 1000000. – ChrisR May 05 '23 at 19:34
  • in any case: really nice animation work :) – ChrisR May 05 '23 at 19:39
  • even if i change it to 1 it still wont work – byaruhaf May 05 '23 at 21:36
  • just to clarify: your question title says that opacity works, but offset does not. For me it's the other way round. Offset does work, and at the end of the animation the little circle flies off upwards. – ChrisR May 05 '23 at 21:38
  • opacity can be delayed by the duration while Offset cant be delayed by the duration – byaruhaf May 05 '23 at 21:45

1 Answers1

0

You can use explicit animation where the toggle is changed:

    func animate() {
        var iteration = 0
        
        Timer.scheduledTimer(withTimeInterval: 0.25, repeats: true) { timer in
            currentIndex = (currentIndex + 1) % 12
            
            iteration += 1
            if iteration == 30 {
                timer.invalidate()
                completed = true
                currentIndex = -1
                DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) {
                    withAnimation(.easeIn(duration: 5)) { // << HERE
                        isVisible = false
                    }
                }
            }
        }
    }
ChrisR
  • 9,523
  • 1
  • 8
  • 26