过渡动画在 SwiftUI 中不起作用
Transition animation not working in SwiftUI
我正在尝试创建一个非常简单的过渡动画,通过点击按钮 shows/hides 在屏幕中央显示一条消息:
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)
}
}
}
}
根据.transition(.opacity)
动画的文档
A transition from transparent to opaque on insertion, and from opaque
to transparent on removal.
消息应在 showMessage
状态 属性 变为真时淡入,当变为假时淡出。就我而言,情况并非如此。该消息以淡入淡出动画显示,但隐藏时根本没有动画。有什么想法吗?
编辑:查看下面从模拟器中获取的 gif 中的结果。
我更喜欢 Scott Gribben 的回答(见下文),但由于我无法删除这个(由于绿色勾选),我将保持原始答案不变。不过,我会争辩说,我确实认为这是一个错误。人们会期望 zIndex 由代码中出现的顺序视图隐式分配。
要解决这个问题,您可以将 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)
}
}
}
}
}
问题是当视图在 ZStack 中来来去去时,它们的 "zIndex" 不会保持不变。发生的事情是,当 "showMessage" 从 true 变为 false 时,带有 "Hello World" 文本的 VStack 被放在堆栈的底部,黄色立即被绘制在它的顶部。它实际上正在淡出,但它是在黄色后面淡出的,所以你看不到它。
要修复它,您需要为堆栈中的每个视图明确指定 "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)
}
}
}
}
我的发现是不透明度过渡并不总是有效。 (然而,幻灯片与 .animation 结合使用。)
.transition(.opacity) //does not always work
如果我将其编写为自定义动画,它确实有效:
.transition(AnyTransition.opacity.animation(.easeInOut(duration: 0.2)))
.zIndex(1)
我认为这是 canvas 的问题。今天早上我在玩转换,虽然在 canvas 上不起作用,但它们似乎在模拟器中起作用。试一试。我已经向 Apple 报告了这个错误。
zIndex
可能会导致动画被打断时断掉。将要应用过渡的视图包装在 VStack
、HStack
或任何其他容器中。
我在 swiftUI_preview 中发现了一个动画错误。当您在代码中使用过渡动画并希望在 SwiftUI_preview 中看到它时,它不会显示动画或仅在某些视图随动画消失时显示。为了解决这个问题,你只需要在 VStack 的预览中添加你的视图。像这样:
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()
}
}
}
在此之后,所有动画都会发生。
我刚刚放弃了 .transition。它只是不工作。我改为动画视图的偏移量,更可靠:
首先我为偏移创建了一个状态变量:
@State private var offset: CGFloat = 200
其次,我为它设置了 VStack 的偏移量。然后,在其 .onAppear() 中,我使用动画将偏移量改回 0:
VStack{
Spacer()
HStack{
Spacer()
Image("MyImage")
}
}
.offset(x: offset)
.onAppear {
withAnimation(.easeOut(duration: 2.5)) {
offset = 0
}
}
下面的代码应该可以工作。
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()
}
}
}
我正在尝试创建一个非常简单的过渡动画,通过点击按钮 shows/hides 在屏幕中央显示一条消息:
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)
}
}
}
}
根据.transition(.opacity)
动画的文档
A transition from transparent to opaque on insertion, and from opaque to transparent on removal.
消息应在 showMessage
状态 属性 变为真时淡入,当变为假时淡出。就我而言,情况并非如此。该消息以淡入淡出动画显示,但隐藏时根本没有动画。有什么想法吗?
编辑:查看下面从模拟器中获取的 gif 中的结果。
我更喜欢 Scott Gribben 的回答(见下文),但由于我无法删除这个(由于绿色勾选),我将保持原始答案不变。不过,我会争辩说,我确实认为这是一个错误。人们会期望 zIndex 由代码中出现的顺序视图隐式分配。
要解决这个问题,您可以将 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)
}
}
}
}
}
问题是当视图在 ZStack 中来来去去时,它们的 "zIndex" 不会保持不变。发生的事情是,当 "showMessage" 从 true 变为 false 时,带有 "Hello World" 文本的 VStack 被放在堆栈的底部,黄色立即被绘制在它的顶部。它实际上正在淡出,但它是在黄色后面淡出的,所以你看不到它。
要修复它,您需要为堆栈中的每个视图明确指定 "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)
}
}
}
}
我的发现是不透明度过渡并不总是有效。 (然而,幻灯片与 .animation 结合使用。)
.transition(.opacity) //does not always work
如果我将其编写为自定义动画,它确实有效:
.transition(AnyTransition.opacity.animation(.easeInOut(duration: 0.2)))
.zIndex(1)
我认为这是 canvas 的问题。今天早上我在玩转换,虽然在 canvas 上不起作用,但它们似乎在模拟器中起作用。试一试。我已经向 Apple 报告了这个错误。
zIndex
可能会导致动画被打断时断掉。将要应用过渡的视图包装在 VStack
、HStack
或任何其他容器中。
我在 swiftUI_preview 中发现了一个动画错误。当您在代码中使用过渡动画并希望在 SwiftUI_preview 中看到它时,它不会显示动画或仅在某些视图随动画消失时显示。为了解决这个问题,你只需要在 VStack 的预览中添加你的视图。像这样:
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()
}
}
}
在此之后,所有动画都会发生。
我刚刚放弃了 .transition。它只是不工作。我改为动画视图的偏移量,更可靠:
首先我为偏移创建了一个状态变量:
@State private var offset: CGFloat = 200
其次,我为它设置了 VStack 的偏移量。然后,在其 .onAppear() 中,我使用动画将偏移量改回 0:
VStack{
Spacer()
HStack{
Spacer()
Image("MyImage")
}
}
.offset(x: offset)
.onAppear {
withAnimation(.easeOut(duration: 2.5)) {
offset = 0
}
}
下面的代码应该可以工作。
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()
}
}
}