Search code examples
iosswiftanimation

Swift Animation does not work when reentering screen


I have built a view with an animation and everything seemed to work fine, but then I found out that it only works for the first time and not for the second. As I am a newbie in Swift programming I don't have an idea where the problem is. I viewed different tutorials for animations and all worked the same way as I have implemented it. I wrote a small sample to reproduce the issue:

import SwiftUI

struct BugSample: View {
    var body: some View {
        TabView {
            ContentView()
                .tabItem {
                    Label("BUG", systemImage: "checkmark")
                }
        }
    }
}

struct Person: Identifiable, Hashable {
    var id: UUID
    var name: String
    
    init(id: UUID = UUID(), name: String) {
        self.id = id
        self.name = name
    }
}

struct ContentView: View {
    @State private var selectedPerson: Person?
    
    var body: some View {
        NavigationSplitView {
            PersonsListView(selectedPerson: $selectedPerson)
        } detail: {
            if let selectedPerson {
                GalleryView()
            } else {
                Text("Should not reach here")
            }
        }
    }
}

struct PersonsListView: View {
    @Binding var selectedPerson: Person?
    
    var data = [Person(name: "A"), Person(name: "B"), Person(name: "C")]
    
    var body: some View {
        List(data, selection: $selectedPerson) { entry in
            Text(entry.name)
                .onTapGesture {
                    selectedPerson = entry
                }
                .listRowSeparator(.hidden)
        }
    }
}

struct GalleryView: View {
    var body: some View {
        NavigationStack {
            VStack(spacing: 20) {
                ProgressView()
                CircularView()
            }
        }
    }
}

struct CircularView: View {
    @State var scale = 1.0

    var body: some View {
        Circle()
            .frame(width: 200, height: 200)
            .scaleEffect(scale)
            .onAppear {
                let baseAnimation = Animation.easeInOut(duration: 1)
                let repeated = baseAnimation.repeatForever(autoreverses: true)

                withAnimation(repeated) {
                    scale = 0.5
                }
            }
    }
}

When you click on one of the 3 entries, then the circle will animate correctly and when I go back and open the screen again, it won't work. I have added also a ProgressView to check if this works and it is spinning around, so maybe the issue lies within the code. My first suggestion was the NavigationSplitView because I have seen that there is an issue with a missing animation, but it is different to my issue (https://forums.developer.apple.com/forums/thread/728132). Has anyone an idea for that problem, I tried several ways the last hours but I don't find a way to get the animation work again.


Solution

  • For some reason, .onAppear (and also onDisappear, when present) are being called, but the animation doesn't restart the second time.

    To fix, trigger the animation using task instead:

    Circle()
        .frame(width: 200, height: 200)
        .scaleEffect(scale)
        .task { @MainActor in
            let baseAnimation = Animation.easeInOut(duration: 1)
            let repeated = baseAnimation.repeatForever(autoreverses: true)
    
            withAnimation(repeated) {
                scale = 0.5
            }
        }