Swiftui Conditional View Transitions Are Not Working

SwiftUI conditional view will not animate/transition

Place your .transition on the container of the views that will switch, not each conditional view. Here's a trivial example from some code I have done (which works).

In the main View that needs to transition conditionally:

import SwiftUI

struct AppWrapperView: View {

@State var showFirstRun:Bool = true

var body: some View {
ZStack {
if (showFirstRun) {
FirstRunView(showFirstRun: $showFirstRun)
} else {
Text("Some other view")
}
}
.transition(.slide)
}
}

Then, somewhere in the view that triggers the change in condition:

import SwiftUI

struct FirstRunView: View {

@Binding var showFirstRun:Bool

var body: some View {

Button(action: {
withAnimation {
self.showFirstRun = false
}
}) {
Text("Done")
}
}
}

Transition animation behavior is not as expected in SwiftUI with double If and if else conditional

I found a solution for this issue. The difference likely has to do with how the SwiftUI Result Builder translates the if-else statement into buildIf, buildEither, etc vs. how the if-else statements are translated. See: https://jasonzurita.com/swiftui-if-statement/

If you explicitly define asymmetric transitions in the if-else statement:

VStack {
homeHeader

columnTitles

if !showPortfolio {
allCoinsListView
.transition(.asymmetric(insertion: .move(edge: .leading),
removal: .move(edge: .trailing)))
} else {
portfolioListView
.transition(.asymmetric(insertion: .move(edge: .trailing),
removal: .move(edge: .leading)))
}

Spacer(minLength: 0)
}

I found the answer to this issue thanks to this post:
SwiftUI Switch Statement Transition Behavior is not as expected

SwiftUI Conditional View Transitions are not working

  1. change group to ZStack

  2. add animation somewhere.

                class ApplicationHostingViewModel: ObservableObject {
    @Published var value: Bool = false
    }

    struct ApplicationHostingView: View {
    // view model env obj
    @ObservedObject var applicationHostingViewModel : ApplicationHostingViewModel
    var body: some View {
    ZStack {

    if applicationHostingViewModel.value {
    LoginView()
    .transition(.move(edge: .leading))
    } else {
    IntroView(applicationHostingViewModel:applicationHostingViewModel)
    }
    }
    }
    }

    struct IntroView: View {
    // view model env obj
    @ObservedObject var applicationHostingViewModel : ApplicationHostingViewModel
    var body: some View {
    Button(action: {
    withAnimation(.default){
    self.applicationHostingViewModel.value = true} }) {
    Text("Continue")
    }
    }
    }

    struct LoginView: View {
    var body: some View {
    Text("Hello World").frame(maxWidth: .infinity, maxHeight: .infinity)
    }
    }

Transition animation not working in SwiftUI

The problem is that when views come and go in a ZStack, their "zIndex" doesn't stay the same. What is happening is that the when "showMessage" goes from true to false, the VStack with the "Hello World" text is put at the bottom of the stack and the yellow color is immediately drawn over top of it. It is actually fading out but it's doing so behind the yellow color so you can't see it.

To fix it you need to explicitly specify the "zIndex" for each view in the stack so they always stay the same - like so:

struct ContentView: View {
@State private var showMessage = false

var body: some View {
ZStack {
Color.yellow.zIndex(0)

VStack {
Spacer()
Button(action: {
withAnimation(.easeOut(duration: 3)) {
self.showMessage.toggle()
}
}) {
Text("SHOW MESSAGE")
}
}.zIndex(1)

if showMessage {
Text("HELLO WORLD!")
.transition(.opacity)
.zIndex(2)
}
}
}
}

How to animate a view transition on a conditional view?

The .animation modifier should be applied to a container owning conditional view, so it could animate appear/disapper transition, like

    VStack {

if S1 {
VX("V1")
.transition(transition)
}
// ... other code
}
.animation(.easeOut, value: S1) // < here !!

Conditional animation in SwiftUI stops working

You can specify Animation in the withAnimation block and create separate functions for starting/stopping the animation.

Here is a possible solution:

struct ContentView: View {
@State private var done = false
@State private var animationAmount: CGFloat = 0

var body: some View {
VStack {
Toggle("Done", isOn: $done)
plusImage
.opacity(done ? 0.6 : 1)
.foregroundColor(done ? .gray : .green)
}
.onAppear(perform: startAnimation)
.onChange(of: done) { done in
if done {
stopAnimation()
} else {
startAnimation()
}
}
}

var plusImage: some View {
Image(systemName: "plus")
.font(.system(size: 40))
.padding()
.overlay(
Circle()
.stroke(Color.gray, lineWidth: 3)
.scaleEffect(1 + animationAmount)
)
}
}
private extension ContentView {
func startAnimation() {
withAnimation(Animation.easeInOut(duration: 1).repeatForever()) {
animationAmount = 0.1
}
}

func stopAnimation() {
withAnimation {
animationAmount = 0
}
}
}

SwiftUI Switch Statement Transition Behavior is not as expected

The difference likely has to do with how the SwiftUI result builder translates the if statement into buildIf, buildEither, etc vs. how the switch statements are translated. See: https://jasonzurita.com/swiftui-if-statement/

It looks like you can get the behavior to match the if statements if you explicitly define asymmetric transitions in the switch statement:

switch exampleStep {

case .stepOne:
Rectangle()
.foregroundColor(Color.green)
.frame(width: 100, height: 100)
.transition(.asymmetric(insertion: .move(edge: .leading), removal: .move(edge: .trailing)))

case .stepTwo:
Rectangle()
.foregroundColor(Color.red)
.frame(width: 100, height: 100)
.transition(.asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .leading)))

}

How to add transition for conditionally rendered view in SwiftUI?

It might be better to keep the animation-related logic (like withAnimation) inside the view.

A possible solution is to create a custom Binding:

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

var binding: Binding<Bool> {
.init(get: {
viewModel.showRow
}, set: { newValue in
withAnimation {
viewModel.showRow = newValue
}
})
}

var body: some View {
List {
Text("Text")
Toggle("Toggle", isOn: binding)
.toggleStyle(SwitchToggleStyle(tint: Color.red))
if viewModel.showRow {
Text("Hidden Text")
}
}
.listStyle(GroupedListStyle())
}
}


Related Topics



Leave a reply



Submit