SwiftUI 动画无法使用动画(_:值:)
SwiftUI animation not working using animation(_:value:)
在 SwiftUI 中,我使用 animation(_:)
修饰符设法在首次将视图绘制到屏幕时使按钮具有动画效果,该修饰符在 macOS 12 中已弃用。
我尝试用新的 animation(_:value:)
修饰符替换它,但这次没有任何反应:
所以这不起作用:
struct ContentView: View {
@State var isOn = false
var body: some View {
Button("Press me") {
isOn.toggle()
}
.animation(.easeIn, value: isOn)
.frame(width: 300, height: 400)
}
}
但这是可行的。为什么?
struct ContentView: View {
var body: some View {
Button("Press me") {
}
.animation(.easeIn)
.frame(width: 300, height: 400)
}
}
第二个示例在视图显示时为按钮设置动画,而第一个示例什么都不做
animation(_:)
和 animation(_:value:)
之间的区别很简单。前者是隐性的,后者是显性的。 animation(_:)
的隐含性质意味着任何时候任何事情发生变化,它都会做出反应。它遇到的另一个问题是试图猜测您想要制作动画的内容。因此,这可能是不稳定和意外的。还有一些其他问题,所以 Apple 已将其弃用。
animation(_:value:)
是显式动画。它只会在你给它的值改变时触发。这意味着您不能只是将其粘贴在视图上并期望视图在出现时具有动画效果。您需要更改 .onAppear()
中的值或使用一些在视图出现时自然更改的值来触发动画。您还需要让一些修饰符专门对更改后的值做出反应。
struct ContentView: View {
@State var isOn = false
//The better route is to have a separate variable to control the animations
// This prevents unpleasant side-effects.
@State private var animate = false
var body: some View {
VStack {
Text("I don't change.")
.padding()
Button("Press me, I do change") {
isOn.toggle()
animate = false
// Because .opacity is animated, we need to switch it
// back so the button shows.
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
animate = true
}
}
// In this case I chose to animate .opacity
.opacity(animate ? 1 : 0)
.animation(.easeIn, value: animate)
.frame(width: 300, height: 400)
// If you want the button to animate when the view appears, you need to change the value
.onAppear { animate = true }
}
}
}
跟进问题:基于对象的 属性 的动画正在处理视图本身,但是当我通过父视图中的 ForEach 传递该视图的数据时,动画修饰符父视图中的那个对象不工作。它甚至不会编译。这些对象恰好是 NSManagedObjects,但我想知道这是否不是问题所在,是修饰符直接作用于子视图,而不作用于父视图中传递的版本。任何见解将不胜感激
// child view
struct TileView: View {
@ObservedObject var tile: Tile
var body: some View {
Rectangle()
.fill(tile.fillColor)
.cornerRadius(7)
.overlay(
Text(tile.word)
.bold()
.font(.title3)
.foregroundColor(tile.fillColor == .myWhite ? .darkBlue : .myWhite)
)
// .animation(.easeInOut(duration: 0.75), value: tile.arrayPos)
// this modifier worked here
}
}
struct GridView: View {
@ObservedObject var game: Game
let columns: [GridItem] = Array(repeating: .init(.flexible()), count: 4)
var body: some View {
GeometryReader { geo in
LazyVGrid(columns: columns) {
ForEach(game.tilesArray, id: \.self) { tile in
Button(action: {
tile.toggleSelectedStatus()
moveTiles() <- this changes their array position (arrayPos), and
the change in position should be animated
}) {
TileView(tile: tile)
.frame(height: geo.size.height * 0.23)
}
.disabled(tile.status == .solved || tile.status == .locked)
.animation(.easeInOut(duration: 0.75), value: arrayPos)
.zIndex(tile.status == .locked ? 1 : 0)
}
}
}
}
}
在 SwiftUI 中,我使用 animation(_:)
修饰符设法在首次将视图绘制到屏幕时使按钮具有动画效果,该修饰符在 macOS 12 中已弃用。
我尝试用新的 animation(_:value:)
修饰符替换它,但这次没有任何反应:
所以这不起作用:
struct ContentView: View {
@State var isOn = false
var body: some View {
Button("Press me") {
isOn.toggle()
}
.animation(.easeIn, value: isOn)
.frame(width: 300, height: 400)
}
}
但这是可行的。为什么?
struct ContentView: View {
var body: some View {
Button("Press me") {
}
.animation(.easeIn)
.frame(width: 300, height: 400)
}
}
第二个示例在视图显示时为按钮设置动画,而第一个示例什么都不做
animation(_:)
和 animation(_:value:)
之间的区别很简单。前者是隐性的,后者是显性的。 animation(_:)
的隐含性质意味着任何时候任何事情发生变化,它都会做出反应。它遇到的另一个问题是试图猜测您想要制作动画的内容。因此,这可能是不稳定和意外的。还有一些其他问题,所以 Apple 已将其弃用。
animation(_:value:)
是显式动画。它只会在你给它的值改变时触发。这意味着您不能只是将其粘贴在视图上并期望视图在出现时具有动画效果。您需要更改 .onAppear()
中的值或使用一些在视图出现时自然更改的值来触发动画。您还需要让一些修饰符专门对更改后的值做出反应。
struct ContentView: View {
@State var isOn = false
//The better route is to have a separate variable to control the animations
// This prevents unpleasant side-effects.
@State private var animate = false
var body: some View {
VStack {
Text("I don't change.")
.padding()
Button("Press me, I do change") {
isOn.toggle()
animate = false
// Because .opacity is animated, we need to switch it
// back so the button shows.
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
animate = true
}
}
// In this case I chose to animate .opacity
.opacity(animate ? 1 : 0)
.animation(.easeIn, value: animate)
.frame(width: 300, height: 400)
// If you want the button to animate when the view appears, you need to change the value
.onAppear { animate = true }
}
}
}
跟进问题:基于对象的 属性 的动画正在处理视图本身,但是当我通过父视图中的 ForEach 传递该视图的数据时,动画修饰符父视图中的那个对象不工作。它甚至不会编译。这些对象恰好是 NSManagedObjects,但我想知道这是否不是问题所在,是修饰符直接作用于子视图,而不作用于父视图中传递的版本。任何见解将不胜感激
// child view
struct TileView: View {
@ObservedObject var tile: Tile
var body: some View {
Rectangle()
.fill(tile.fillColor)
.cornerRadius(7)
.overlay(
Text(tile.word)
.bold()
.font(.title3)
.foregroundColor(tile.fillColor == .myWhite ? .darkBlue : .myWhite)
)
// .animation(.easeInOut(duration: 0.75), value: tile.arrayPos)
// this modifier worked here
}
}
struct GridView: View {
@ObservedObject var game: Game
let columns: [GridItem] = Array(repeating: .init(.flexible()), count: 4)
var body: some View {
GeometryReader { geo in
LazyVGrid(columns: columns) {
ForEach(game.tilesArray, id: \.self) { tile in
Button(action: {
tile.toggleSelectedStatus()
moveTiles() <- this changes their array position (arrayPos), and
the change in position should be animated
}) {
TileView(tile: tile)
.frame(height: geo.size.height * 0.23)
}
.disabled(tile.status == .solved || tile.status == .locked)
.animation(.easeInOut(duration: 0.75), value: arrayPos)
.zIndex(tile.status == .locked ? 1 : 0)
}
}
}
}
}