SwiftUI:控制动画堆栈

这次,我想将您已经单独理解的两件事放在一起,但是放在一起可能会有点伤脑筋。

之前,我们研究了修饰符的顺序的重要性。因此,如果我们这样编写代码:

Button("Tap Me") {
    // do nothing
}
.background(Color.blue)
.frame(width: 200, height: 200)
.foregroundColor(.white)

结果将看起来与这样的代码不同:

Button("Tap Me") {
    // do nothing
}
.frame(width: 200, height: 200)    
.background(Color.blue)
.foregroundColor(.white)

这是因为如果我们在调整Frame之前为背景着色,则仅对原始空间着色,而不对扩展空间着色。如果您还记得的话,其根本原因是SwiftUI用修饰符包装视图的方式,使我们可以多次应用相同的修饰符——我们多次重复执行background()padding()来创建条纹边框效果。

这就是概念一:修饰符顺序很重要,因为SwiftUI会按照修饰符的应用顺序包装视图。

概念二是我们可以对视图应用animation()修饰符,以使其隐式地对更改进行动画处理。

为了演示这一点,我们可以修改按钮代码,以便根据某些状态显示不同的颜色。首先,我们定义状态:

@State private var enabled = false

在按钮的Action中我们可以在true和false之间进行切换:

self.enabled.toggle()

然后,我们可以在background()修饰符内使用一个条件值,这样按钮可以是蓝色或红色:

.background(enabled ? Color.blue : Color.red)

最后,我们向按钮添加animation()修饰符以使这些更改具有动画效果:

.animation(.default)

如果您运行该代码,则会看到点击按钮会在蓝色和红色之间为其设置动画的颜色。

因此:顺序修饰符很重要,我们可以将一个修饰符多次附加到视图上,并且可以通过animation()修饰符使隐式动画发生。到目前为止都清楚吗?

那好。保护号自己,因为这可能会让你受伤。

您可以多次附加animation()修饰符,并且使用顺序很重要。

为了说明这一点,我希望您在所有其他修饰符之后将这个修饰符添加到按钮中:

.clipShape(RoundedRectangle(cornerRadius: enabled ? 60 : 0))

这将导致按钮根据enabled的布尔值的状态在正方形和圆角矩形之间变化。

运行该程序时,您会发现点击按钮会使其在红色和蓝色之间进行动画处理,但是会在正方形和圆角矩形之间进行跳转——该部分不会进行动画处理。

希望您能看到下一步:我希望您将clipShape()修饰符移到动画之前,如下所示:

.frame(width: 200, height: 200)
.background(enabled ? Color.blue : Color.red)
.foregroundColor(.white)
.clipShape(RoundedRectangle(cornerRadius: enabled ? 60 : 0))
.animation(.default)

现在,当您运行代码时,背景颜色和剪辑形状都将进行动画处理。

因此,我们应用动画的顺序很重要:只有在animation()修饰符之前发生的更改会变添加动画。

现在,有趣的部分是:如果我们应用多个animation()修饰符,则每个修饰符都会控制所有内容,直到下一个动画为止。这使我们能够以各种不同的方式为状态变化设置动画,而不是为所有属性统一设置。

例如,我们可以使用默认动画来进行颜色更改,但是对形状改变使用弹簧效果:

Button("Tap Me") {
    self.enabled.toggle()
}
.frame(width: 200, height: 200)
.background(enabled ? Color.blue : Color.red)
.animation(.default)
.foregroundColor(.white)
.clipShape(RoundedRectangle(cornerRadius: enabled ? 60 : 0))
.animation(.interpolatingSpring(stiffness: 10, damping: 1))

您可以根据需要在构造设计时使用任意数量的animation()修饰符,这使我们可以将一种状态更改分成所需的多个分段。

为了更好的控制,可以通过将nil传递给修饰符来完全禁用动画。例如,您可能希望颜色立即发生变化,但剪辑形状保留其动画,在这种情况下,您可以这样编写:

Button("Tap Me") {
    self.enabled.toggle()
}
.frame(width: 200, height: 200)
.background(enabled ? Color.blue : Color.red)
.animation(nil)
.foregroundColor(.white)
.clipShape(RoundedRectangle(cornerRadius: enabled ? 60 : 0))
.animation(.interpolatingSpring(stiffness: 10, damping: 1))

没有多个animation()修饰符将无法实现这种控制——如果您尝试在动画之后去改变background(),您会发现它只会撤消clipShape()的工作。

Button("Tap Me") {
    self.enabled.toggle()
}
.frame(width: 200, height: 200)
.background(enabled ? Color.blue : Color.red)
.foregroundColor(.white)
.clipShape(RoundedRectangle(cornerRadius: enabled ? 60 : 0))
.animation(.interpolatingSpring(stiffness: 10, damping: 5))
.background(enabled ? Color.blue : Color.red)

最终的的形状将会是一成不变的矩形。

译自Controlling the animation stack

Previous: 创建显式动画 Hacking with iOS: SwiftUI Edition Next: 动画手势

赏我一个赞吧~~~

你可能感兴趣的:(SwiftUI:控制动画堆栈)