在 SwiftUI 中为什么应该尽量避免直接使用 animation(_:) 方法创建动画?

Posted 大熊猫侯佩

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在 SwiftUI 中为什么应该尽量避免直接使用 animation(_:) 方法创建动画?相关的知识,希望对你有一定的参考价值。

概览

在 SwiftUI 开发中,视图动画的点缀可以让界面更加鲜活灵动。

不过,如果在视图上错误的应用动画可能会适得其反,不但让用户使用时一头雾水,也让开发者在调试时“怒发冲冠”。

如上图所示:在用户关闭 sheet 弹出视图最后返回主界面时,会发现原本应该消失不见的蓝色光圈却向屏幕左上角反复“跳动”。

这是因为你直接在视图上应用隐式动画所导致的:

那么,到底为什么会发生这种情况呢?

Let’s find out!!! 😎


从 SwiftUI 动画的类型谈起

SwiftUI 中视图动画分为两种:隐式和显式动画。

下面,我们使用 animation(_: ) 修改器方法为视图 BigView 设置了隐式动画:

BigView(...)
	.animation(.spring())

而显式动画是把引发动画状态改变的代码放在 withAnimation 方法闭包中:

BigView(...)
    .scaleEffect(isImportant ? 1.5 : 1.0)                  
	.onTapGesture 
	    withAnimation 
	        isImportant.toggle()
	    
	

为什么博文开头例子中的动画会出现那样的问题呢?

答案就是:它使用隐式动画而不是显式动画!

隐式动画貌似简单便捷,实际会有很多不良副作用。当多个动画状态互相交织或者多个视图动画状态之间互相影响时,使用隐式动画会导致动画边界混乱化。

还拿博文开头的例子来说吧:

Circle()
	.stroke(hlColor, lineWidth: 3.0)
	.background(Circle().fill(hlColor.opacity(0.5)).brightness(0.5))
	.frame(width: rect.width, height: rect.height)
	.scaleEffect(startAnim ? 2.0 : 1.0)
	.opacity(startAnim ? 0.0 : 1.0)
	.animation(
	    Animation.easeOut(duration: 1.0)
	        .repeatForever(autoreverses: false)
	)

如上代码所示,蓝色光圈 Circle 视图的动画实际被多个状态所影响,其中就包括 rect 和 startAnim 两个状态。

我们在 Circle 视图上应用了隐式动画,这会导致所有状态都会产生动画效果,但我们并不希望 rect 状态引发动画,因为这会导致开头蓝色光圈向屏幕左上角反复“跳动”的问题。

所以,更好的解决方案是:只响应 startAnim 状态引发的动画,使用显式动画,这很好解决。

首先,删除 Circle 视图最后的隐式动画,接着为 startAnim 状态添加显式动画:

// 需要更新 startAnim 状态时应用显式动画:
withAnimation(.easeOut(duration: 1.0).repeatForever(autoreverses: false)) 
    startAnim.toggle()

更精确的动画方式

其实,SwiftUI 早就已经为我们考虑到了这个问题,animation 方法有另一个更为“精确”的重写方法 animation(_:value:)。

我们可以这样使用它来解决上面的问题:

Circle()
	.stroke(hlColor, lineWidth: 3.0)
	.background(Circle().fill(hlColor.opacity(0.5)).brightness(0.5))
	.frame(width: rect.width, height: rect.height)
	.scaleEffect(startAnim ? 2.0 : 1.0)
	.opacity(startAnim ? 0.0 : 1.0)
	// 明确告知系统只有 startAnim 状态会引发动画
	.animation(.easeOut(duration: 1.0)
	    .repeatForever(autoreverses: false), value: startAnim)

如上所示:我们在 Circle 视图上明确告知系统,只有 startAnim 状态会引发动画效果,而其他的状态统统无视。

看一下修复后的动画效果:

现在,利用显式动画和更为精确的 animation(_:value:) 修改器方法,我们可以为任意视图更精确的实施“外科手术”般的动画效果,避免额外状态改变时的干扰,棒棒哒!💯

总结

在本篇博文中,我们讨论了 SwiftUI 中直接在视图上应用 animation(_: ) 方法创建动画的弊端,同时提出了更好、更精准的动画实现。

感谢观赏,再会!😎

以上是关于在 SwiftUI 中为什么应该尽量避免直接使用 animation(_:) 方法创建动画?的主要内容,如果未能解决你的问题,请参考以下文章

在 SwiftUI 中为什么应该尽量避免直接使用 animation(_:) 方法创建动画?

Unity脚本类为啥要尽量避免继承MonoBehaviour类

Java中的异常捕捉try为什么要尽量使用具体标准的异常,为什么不要直接使用ExceptionRuntimeExceptionErrorThrowable,避免在try catch中进行业务编码

Java中的异常捕捉try为什么要尽量使用具体标准的异常,为什么不要直接使用ExceptionRuntimeExceptionErrorThrowable,避免在try catch中进行业务编码

Unity脚本类为啥要尽量避免继承MonoBehaviour类

Unity脚本类为啥要尽量避免继承MonoBehaviour类