1
votes

In my app I have a ScrollView that holds a VStack. The VStack has an animation modifier. It also has one conditional view with a transition modifier. The transition works. However, when the view first appears, the content scales up with a starting width of 0. You can see this on the green border in the video below. This only happens if the VStack is inside the ScrollView or the VStack has the animation modifier.

I have tried different other things like setting a fixedSize or setting animation(nil) but I cannot get it to work.

I don't care if there is an animation at the beginning or if the view transitions onto the screen. But I definitely do not want the bad animation at the beginning. When I click on the button, the Text and the Button should both animate.

I also tested this behaviour with the following simplified code.

struct ContentView: View {
    @ObservedObject var viewModel = ViewModel()

    var body: some View {
        ScrollView {
            VStack(spacing: 32) {
                if self.viewModel.shouldShowText {
                    Text("Hello, World! This is a some text.")
                        .transition(
                            AnyTransition
                                .move(edge: .top)
                                .combined(
                                    with:
                                        .offset(
                                            .init(width: 0, height: -100)
                                        )
                                )
                        )
                }

                Button(
                    action: {
                        self.viewModel.didSelectButton()
                    },
                    label: {
                        Text("Button")
                            .font(.largeTitle)
                    }
                )
            }
            .border(Color.green)
            .animation(.easeInOut(duration: 5))
            .border(Color.red)

            HStack { Spacer() }
        }
        .border(Color.blue)
    }
}

class ViewModel: ObservableObject {
    @Published private var number: Int = 0

    var shouldShowText: Bool {
        return self.number % 2 == 0
    }

    func didSelectButton() {
        self.number += 1
    }
}
1

1 Answers

3
votes

It works fine with Xcode 12 / iOS 14 (and stand-alone and in NavigationView), so I assume it is SwiftUI bug in previous versions.

Anyway, try to make animation be activated by value as shown below (tested & works)

  // ... other code
}
.border(Color.green)
.animation(.easeInOut(duration: 5), value: viewModel.number)   // << here !!
.border(Color.red)

and for this work it needs to make available published property for observation

class BlockViewModel: ObservableObject {
    @Published var number: Int = 0             // << here !!
 
    // ... other code