iosswiftanimationswiftuiios16

Transition animation not working in iOS16 but was working in iOS15


I have a SwiftUI Form with a custom chart view (not Swift Charts). A long press toggles to a different type of chart. These charts use the .transition(.slide) modifier. In iOS 15 these transitioned as expected on a long press, but in iOS 16 they do not.

Persisted state property (an enum):

@AppStorage("chartType") var chartType: ChartType = .chartA

The Form part of the body property:

Form {
    
    // Other sections
    
    Section {
        switch chartType {
            case .chartA:
                ChartViewA()
                    .transition(.slide)
            case .chartB:
                ChartViewB()
                    .transition(.slide)
    }
        .onLongPressGesture {
            if chartType == .chartA {
                withAnimation {
                    summaryChartType = .chartB
                }
            } else {
                withAnimation {
                    summaryChartType = .chartA
                }
            }
        }

Unfortunately adding animation modifiers like .animation(.spring(), value: chartType) makes no difference.

I would be grateful for advice on why this might have worked in iOS 15 but not in iOS 16, and what I could do to restore animation here.


Solution

  • In iOS 16, there appears to be a problem with @AppStorage vars and animation. Here is one possible workaround. Use @State var for animation, and save it to an @AppStorage variable with .onChange():

    enum ChartType: String {
        case chartA, chartB
    }
    
    struct ChartViewA: View {
        var body: some View {
            Color.red
        }
    }
    
    struct ChartViewB: View {
        var body: some View {
            Color.blue
        }
    }
    
    struct ContentView: View {
        @AppStorage("chartType") var chartTypeAS: ChartType = .chartA
        @State private var chartType: ChartType = .chartA
        
        init() {
            // load initial value from persistent storage
            _chartType = State(initialValue: chartTypeAS)
        }
        
        var body: some View {
    
            Form {
                
                // Other sections
                
                Section {
                    VStack {
                        switch chartType {
                        case .chartA:
                            ChartViewA()
                                .transition(.slide)
                        case .chartB:
                            ChartViewB()
                                .transition(.slide)
                        }
                    }
                    .onLongPressGesture {
                        if chartType == .chartA {
                            withAnimation {
                                chartType = .chartB
                            }
                        } else {
                            withAnimation {
                                chartType = .chartA
                            }
                        }
                    }
                }
                .onChange(of: chartType) { value in
                    // persist chart type
                    chartTypeAS = value
                }
            }
        }
    }
    

    Tested in Xcode 14.0 with iPhone 14 simulator running iOS 16.

    Alternatively, you could perform the saving to/restoring from UserDefaults manually.