Not possible to control animations in a Form?
Asked Answered
C

1

6

Is it not meant to be possible to control the animations that take place inside of a Form view? I have here a playground that demonstrates the issue, along with a gif of what happens. As you can see, my transition on the 2nd animated view is completely ignored, and I had to manually slow down the video because the durations are ignored, too.

I don't really want a scaling transition, this was just to demonstrate that no matter what I put in there the animation is the same. Is that expected, or is it a bug? Or am I just doing something totally wrong?

It's also not clear to me why the animation of the VStack is handled so differently than the simple Text field, which slides down nicely while the VStack seems to be getting some combination of .move and .opacity.

animation demo

import SwiftUI
import PlaygroundSupport


struct ContentView: View {

    @State var showGoodAnimation = false
    @State var showBadAnimation = false

    var body: some View {
        Form {
            Toggle(isOn: self.$showGoodAnimation.animation(.easeInOut(duration: 1))) {Text("Yay!")}
            if self.showGoodAnimation {
                Text("I animate beautifully.")
            }
            Toggle(isOn: self.$showBadAnimation.animation(.easeInOut(duration: 1))) {Text("Boo!")}
            if self.showBadAnimation {
                VStack {
                    Text("Hi.").padding()
                    Text("I'm a hot mess.").padding()
                }
                .frame(height: 250)
                .transition(.scale)
            }
            Text("I'm just always here.")
        }
    }
}

PlaygroundPage.current.setLiveView(ContentView())
Cantor answered 23/11, 2019 at 15:35 Comment(4)
Hi - thanks, but this isn’t a tableview, and because those bools are @State variables SwiftUI re-renders the view automatically when they change. You can see in the gif that it works and it animates them, just in a surprising way.Cantor
In Preview & Simulator your code just copy/pasted works well. Xcode 11.2. Probably it is a Playground issue.Diversification
The issue isn’t really that it doesn’t work, it’s that you apparently have no say in how animations work within a form and I’m not sure if that’s intentional or a bug. This is a distillation of something I’ve been trying to do in a larger project for repro purposes. I’ve been testing in the simulator and on devices. No matter what you tell SwiftUI about how you want this to be animated it ignores you.Cantor
any solutions? i am having the same issuePapilionaceous
M
4

At a guess, probably worked around this question some time ago, but for the benefit of those beating their head's against SwiftUI Form and the like now (as I was :-) )

It turns out that Forms, Lists and (no doubt) other components purposely ignore animation customisation because they are "higher-level" SwiftUI View components (unlike V and HStack's).

They do this because SwiftUI's higher-level components are intended to convey semantic information and (more practically) to work well across all platforms. To achieve this, Apple's engineering decision has been to make animation "easy", but (as observed) only to the extent of essentially turning it "on" or "off".

It's likely engineered like this because if a developer wants more control. Then Apple believes encouraging them to use the lower-level components will be less painful than trying to work around the optimisations they have applied to their higher-level view components.

Anyway, for the determined, there is at least one escape hatch via wrapping the View in a container and specifying the .animation(nil) modifier (as mentioned in Asperi's SO answer here.

An example of this is shown below for completeness; personally, I'm avoiding this pattern as I suspect it's a bit of a footgun.

import PlaygroundSupport
import SwiftUI

struct ContentView: View {
    @State var showGoodAnimation = false
    @State var showBadAnimation = false

    var body: some View {
        Form {
            Toggle(isOn: self.$showGoodAnimation.animation(.easeInOut(duration: 1))) { Text("Yay!") }
            if self.showGoodAnimation {
                Text("I animate beautifully.")
            }
            Toggle(isOn: self.$showBadAnimation.animation()) { Text("Boo!") }
            VStack {
                if self.showBadAnimation {
                    List {
                        Text("I animated differently").padding()
                        Text("But am I a footgun?").padding()
                    }
                    .transition(.asymmetric(insertion: .slide, removal: .opacity))
                    .animation(.easeOut(duration: 5))
                }
            }
            .animation(nil)
            .transition(.slide)
            Text("I'm just always here.")
        }
    }
}

PlaygroundPage.current.setLiveView(ContentView())
Marcel answered 18/5, 2021 at 10:7 Comment(1)
Thanks so much for following up here. You're right, I wound up taking a wide berth around this a long time ago, but it's still useful to know how it can be done (and why it might not be a good idea to).Cantor

© 2022 - 2024 — McMap. All rights reserved.