30
votes

L'animation de transition ne fonctionne pas dans SwiftUI

J'essaie de créer une animation de transition très simple qui montre / masque un message au centre de l'écran en appuyant sur un bouton:

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

    var body: some View {
        ZStack {
            Color.yellow

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

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

Selon la documentation de l' .transition(.opacity)

Une transition de transparent à opaque lors de l'insertion, et d'opaque à transparent lors du retrait.

le message doit disparaître lorsque la propriété d'état showMessage devient vraie et disparaître lorsqu'elle devient fausse. Ce n'est pas vrai dans mon cas. Le message apparaît avec une animation de fondu, mais il se cache sans aucune animation. Des idées?

EDIT: Voir le résultat dans le gif ci-dessous tiré du simulateur.

entrez la description de l'image ici


0 commentaires

6 Réponses :


4
votes

J'aime mieux la réponse de Scott Gribben (voir ci-dessous), mais comme je ne peux pas supprimer celle-ci (en raison de la coche verte), je vais simplement laisser la réponse originale inchangée. Je dirais cependant que je considère que c'est un bug. On s'attendrait à ce que le zIndex soit implicitement attribué par les vues de commande qui apparaissent dans le code.


Pour contourner ce problème, vous pouvez incorporer l'instruction if dans un VStack.

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

    var body: some View {
        ZStack {
            Color.yellow

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

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


5 commentaires

Pourquoi ça marche? Pourrait utiliser quelques précisions ici.


@whistler Pas vraiment, c'est juste une solution de contournement pour un bogue. Le résultat du test de différentes alternatives dans l'espoir que le code du framework utilisera un chemin différent pour appliquer la transition.


Mec, ce qui est vraiment étrange à ce sujet, c'est que si je supprime le Color.yellow , cela fonctionne comme prévu. Je dépose des commentaires.


Ce problème me rend fou


@kontiki: c'est très honnête de votre part



72
votes

Le problème est que lorsque les vues vont et viennent dans un ZStack, leur "zIndex" ne reste pas le même. Ce qui se passe, c'est que lorsque "showMessage" passe de vrai à faux, le VStack avec le texte "Hello World" est placé au bas de la pile et la couleur jaune est immédiatement dessinée par-dessus. Il s'estompe en fait, mais il le fait derrière la couleur jaune pour que vous ne puissiez pas le voir.

Pour résoudre ce problème, vous devez spécifier explicitement le "zIndex" pour chaque vue de la pile afin qu'ils restent toujours les mêmes - comme ceci:

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)
        }
    }
}

}


4 commentaires

L'essentiel pour moi est que la vue animée doit être enveloppée dans un conditionnel pour que les animations fonctionnent


Merci beaucoup ... Cela m'a beaucoup aidé ... J'essayais de le réparer comme un copain d'enfer ...


Pourquoi le texte ("HELLO WORLD") irait-il au bas du ZStack lorsque showMessage est défini sur false? Cette solution fonctionne, mais je ne comprends pas vraiment pourquoi.


vous n'avez pas besoin d'ajouter zIndex à toutes vos vues car dans des vues complexes avec de nombreuses vues, c'est très difficile à faire. Je l'ai fait en ajoutant un seul zIndex pour voir que je veux animer et tout fonctionne bien.



31
votes

Mes conclusions sont que les transitions d'opacité ne fonctionnent pas toujours. (Pourtant, une diapositive en combinaison avec une .animation fonctionnera.)

    .transition(AnyTransition.opacity.animation(.easeInOut(duration: 0.2))) 
    .zIndex(1)

Si je l'écris comme une animation personnalisée, cela fonctionne:

    .transition(.opacity) //does not always work


6 commentaires

Cela fonctionne bien avec une toile de fond plein écran.


Je peux confirmer que cela fonctionne. C'est étrange comment une implémentation sur SwiftUI ne fonctionne pas de manière cohérente


Et btw à partir de maintenant (iOS 14 / XCode 12) aucun zIdex n'est requis


C'était la SEULE SOLUTION qui fonctionnait pour moi - zIndex et la syntaxe personnalisée AnyTransition étaient nécessaires pour que mon application fonctionne. Merci beaucoup @Pbk!


meilleure réponse que j'ai vue. il n'est pas nécessaire de zIndex (1) aussi et a fonctionné pour moi. Merci beaucoup


Ce qui est amusant, c'est que si vous enchaînez .transition(.opacity).animation(.easeInOut(duration: 0.2)) cela n'aide pas non plus.



1
votes

Je pense que c'est un problème avec la toile. Je jouais avec les transitions ce matin et même si elles ne fonctionnent pas sur la toile, elles semblent fonctionner dans le simulateur. Essayez ça. J'ai signalé le bogue à Apple.


1 commentaires

C'était mon problème exact! Je me demandais ce qui n'allait pas avec le code. Merci beaucoup!



0
votes

zIndex peut provoquer la zIndex l'animation en cas d'interruption. Enveloppez la vue à laquelle vous souhaitez appliquer la transition dans un VStack , HStack ou tout autre conteneur aura du sens.


0 commentaires

0
votes

J'ai trouvé un bogue dans swiftUI_preview pour les animations. lorsque vous utilisez une animation de transition dans le code et que vous voulez voir que dans SwiftUI_preview, il n'affichera pas d'animations ou s'affichera simplement lorsqu'une vue disparaîtra avec l'animation. pour résoudre ce problème, il vous suffit d'ajouter votre vue en aperçu dans un VStack. comme ça :

struct test_UI: View {
    @State var isShowSideBar = false
    var body: some View {
        ZStack {
            Button("ShowMenu") {
                withAnimation {
                    isShowSideBar.toggle()
                }
                
            }
            if isShowSideBar {
                SideBarView()
                    .transition(.slide)
            }
        }
    }
}
        struct SomeView_Previews: PreviewProvider {
        static var previews: some View {
            VStack {
               SomeView()
            }
        }
    }

après cela, toutes les animations se produiront.


0 commentaires