简体   繁体   English

过渡动画在 SwiftUI 中不起作用

[英]Transition animation not working in SwiftUI

I'm trying to create a really simple transition animation that shows/hides a message in the center of the screen by tapping on a button:我正在尝试创建一个非常简单的过渡动画,通过点击一个按钮在屏幕中央显示/隐藏一条消息:

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

According to the documentation of the .transition(.opacity) animation根据.transition(.opacity)动画的文档

A transition from transparent to opaque on insertion, and from opaque to transparent on removal.插入时从透明到不透明的过渡,在移除时从不透明到透明的过渡。

the message should fade in when the showMessage state property becomes true and fade out when it becomes false.showMessage状态属性变为 true 时消息应该淡入,当它变为 false 时淡出。 This is not true in my case.在我的情况下这不是真的。 The message shows up with a fade animation, but it hides with no animation at all.该消息以淡入淡出动画显示,但根本没有动画隐藏。 Any ideas?有任何想法吗?

EDIT: See the result in the gif below taken from the simulator.编辑:查看下面从模拟器中获取的 gif 中的结果。

在此处输入图像描述

The problem is that when views come and go in a ZStack, their "zIndex" doesn't stay the same.问题是当视图在 ZStack 中来来去去时,它们的“zIndex”不会保持不变。 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.发生的情况是,当“showMessage”从 true 变为 false 时,带有“Hello World”文本的 VStack 被放在堆栈的底部,黄色立即绘制在它的顶部。 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:要修复它,您需要为堆栈中的每个视图显式指定“zIndex”,以便它们始终保持不变 - 如下所示:

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

} }

My findings are that opacity transitions don't always work.我的发现是不透明度转换并不总是有效。 (yet a slide in combination with an .animation will work..) (但幻灯片与 .animation 结合使用会起作用..)

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

If I write it as a custom animation it does work:如果我将其编写为自定义动画,它确实有效:

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

I found a bug in swiftUI_preview for animations.我在 swiftUI_preview 中发现了一个动画错误。 when you use a transition animation in code and want to see that in SwiftUI_preview it will not show animations or just show when some views disappear with animation.当您在代码中使用过渡动画并希望在 SwiftUI_preview 中看到它时,它不会显示动画或仅显示某些视图何时随动画消失。 for solving this problem you just need to add your view in preview in a VStack.为了解决这个问题,您只需在 VStack 的预览中添加您的视图。 like this :像这样 :

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

after this, all animations will happen.在此之后,所有动画都会发生。

I believe this is a problem with the canvas.我相信这是画布的问题。 I was playing around with transitions this morning and while the don't work on the canvas, they DO seem to work in the simulator.今天早上我在玩转场,虽然不在画布上工作,但它们似乎在模拟器中工作。 Give that a try.试试看。 I've reported the bug to Apple.我已经向 Apple 报告了这个错误。

I like Scott Gribben's answer better (see below), but since I cannot delete this one (due to the green check), I'll just leave the original answer untouched.我更喜欢 Scott Gribben 的答案(见下文),但由于我无法删除这个答案(由于绿色检查),我将保持原始答案不变。 I would argue though, that I do consider it a bug.不过,我会争辩说,我确实认为这是一个错误。 One would expect the zIndex to be implicitly assigned by the order views appear in code.人们会期望 zIndex 由代码中出现的订单视图隐式分配。


To work around it, you may embed the if statement inside a VStack.要解决这个问题,您可以将 if 语句嵌入到 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)
                }
            }
        }
    }
}

zIndex may cause the animation to be broken when interrupted. zIndex可能会导致动画在中断时中断。 Wrap the view you wanna apply transition to in a VStack , HStack or any other container will make sense.将要应用转换的视图包装在VStackHStack或任何其他容器中是有意义的。

I just gave up on .transition.我刚刚放弃了.transition。 It's just not working.它只是不工作。 I instead animated the view's offset, much more reliable:我改为动画视图的偏移,更可靠:

First I create a state variable for offset:首先我为偏移量创建一个状态变量:

@State private var offset: CGFloat = 200

Second, I set the VStack's offset to it.其次,我将 VStack 的偏移量设置为它。 Then, in its .onAppear(), I change the offset back to 0 with animation:然后,在其 .onAppear() 中,我使用动画将偏移量改回 0:

        VStack{
            Spacer()
            HStack{
                Spacer()
                Image("MyImage")
            }
        }
        .offset(x: offset)
        .onAppear {
            withAnimation(.easeOut(duration: 2.5)) {
                offset = 0
            }
        }

Below code should work.下面的代码应该可以工作。

import SwiftUI

struct SwiftUITest: View {
    
    @State private var isAnimated:Bool = false
  
    var body: some View {
        ZStack(alignment:.bottom) {
            VStack{
                Spacer()
                Button("Slide View"){
                    withAnimation(.easeInOut) {
                        isAnimated.toggle()
                    }
                    
                }
                Spacer()
                Spacer()
           
            }
            if isAnimated {
                RoundedRectangle(cornerRadius: 16).frame(height: UIScreen.main.bounds.height/2)
                    .transition(.slide)

            }
            
            
        }.ignoresSafeArea()
    }
}

struct SwiftUITest_Previews: PreviewProvider {
    static var previews: some View {
        VStack {
            SwiftUITest()
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM