技术 · 2025年3月15日 0

使用 PhaseAnimator 创建动态多步骤动画

SwiftUI 已经简化了视图动画的创建。一个例子是 matchedGeometryEffect modifier,它使开发人员能够定义两个视图的外观。modifier 计算两个视图之间的差异,并自动为大小和位置变化设置动画。在 iOS 17 中,Apple 继续改进 SwiftUI 框架,并提供了一种名为 PhaseAnimator ,这使我们能够制作更复杂的动画。

在本教程中,我们将探索 PhaseAnimator 并学习如何利用它来创建多步骤动画。

使用 PhaseAnimator 构建简单动画

PhaseAnimator 视图,或 .phaseAnimator 修改器,使您能够生成多步骤动画。通过循环您提供的一系列阶段(每个阶段代表一个不同的步骤),您可以创建动态且引人入胜的动画。

swiftui-phaseanimator-rectangle-demo

让我举一个简单的例子,这样你就会明白如何使用相位动画器。我们将为圆角矩形的变换制作动画。它一开始是一个蓝色矩形,然后放大,颜色变为靛蓝色,并包含 3D 旋转动画。

我们可以使用 RoundedRectangle 视图创建圆角矩形并附加 phaseAnimator 像这样对矩形进行修改:

struct ContentView: View {
   var body: some View {
       RoundedRectangle(cornerRadius: 25.0)
           .frame(height: 200)
           .phaseAnimator([ false, true ]) { content, phase in
               content
                   .scaleEffect(phase ? 1.0 : 0.5)
                   .foregroundStyle(phase ? .indigo : .blue)
          }
  }
}

在阶段动画器中,我们指定两个阶段: falsetrue 。视图构建器闭包接受两个参数。第一个参数是一个代理值,表示修改后的视图。第二个参数表示当前阶段。

当视图最初出现时,第一阶段(即 false ) 处于活动状态。我们将比例设置为原始大小的 50%,并将前景色设置为蓝色。在第二阶段,矩形将缩小回其原始大小,颜色过渡为靛蓝。

阶段动画器自动为这两个阶段之间的变化制作动画。

swiftui-phase-animator-scale-demo

要创建 3D 旋转动画,您可以附加 rotation3DEffect 修饰符 content 如下图所示:

.rotation3DEffect(
   phase ? .degrees(720) : .zero,
                         axis: (x: 0.0, y: 1.0, z: 0.0)
)

如果你想自定义动画, phaseAnimator 还提供了 animation 参数用于定义您喜欢的动画。根据给定的阶段,您可以指定从一个阶段移动到另一个阶段时要使用的动画。以下是示例:

.phaseAnimator([ false, true ]) { content, phase in
   content
       .scaleEffect(phase ? 1.0 : 0.5)
       .foregroundStyle(phase ? .indigo : .blue)
       .rotation3DEffect(
           phase ? .degrees(720) : .zero,
                                 axis: (x: 0.0, y: 1.0, z: 0.0)
      )
} animation: { phase in
   switch phase {
   case true: .smooth.speed(0.2)
   case false: .spring
  }
}

使用枚举定义多步骤动画

在前面的例子中,动画仅包含两个阶段: falsetrue 。然而,在更复杂的动画中,通常会涉及多个步骤或阶段。在这种情况下,枚举是定义动画步骤列表的好方法。

swiftui-multi-step-animation-phaseanimator

让我们考虑一个表情符号图标动画的示例,其步骤如下:

  1. 最初,表情符号图标位于屏幕的中心。
  2. 它放大了 50% 并旋转了 720 度。
  3. 接下来,它向上移动 250 个点,同时向下缩减 20%。
  4. 然后,它向下移动 450 点。下降过程中,它自身旋转 360 度,并缩小 50%。
  5. 最后它又回到原来的位置。

通过这些步骤,我们可以为表情符号图标创建动态动画。

为了实现这个多步骤动画,我们可以定义一个这样的枚举:

enum Phase: CaseIterable {
   case initial
   case rotate
   case jump
   case fall

   var scale: Double {
       switch self {
       case .initial: 1.0
       case .rotate: 1.5
       case .jump: 0.8
       case .fall: 0.5
      }
  }

   var angle: Angle {
       switch self {
       case .initial, .jump: Angle(degrees: 0)
       case .rotate: Angle(degrees: 720)
       case .fall: Angle(degrees: 360)
      }
  }

   var offset: Double {
       switch self {
       case .initial, .rotate: 0
       case .jump: -250.0
       case .fall: 450.0
      }
  }
}

在此枚举中,我们有四种情况,代表动画的不同步骤。在每个阶段,我们对表情符号图标执行缩放、旋转或移动。为此,我们为每个操作定义三个计算属性。在每个属性中,我们为特定动画阶段或步骤指定值。

例如,在“旋转”阶段,表情符号应放大 50% 并旋转 720 度。 scale 属性返回 1.5,并且 angle 财产回报 Angle(degrees: 720)

随着 Phase 枚举,我们现在可以使用阶段动画器轻松地为表情符号制作动画,如下所示:

Text("🐻")
   .font(.system(size: 100))
   .phaseAnimator(Phase.allCases) { content, phase in
       content
           .scaleEffect(phase.scale)
           .rotationEffect(phase.angle)
           .offset(y: phase.offset)

  } animation: { phase in
       switch phase {
       case .initial: .bouncy
       case .rotate: .smooth
       case .jump: .snappy
       case .fall: .interactiveSpring
      }
  }

Phase.allCases 自动通知阶段动画器可用的阶段。根据给定的阶段,表情符号图标将根据计算值进行缩放、旋转和移动。

为了定制动画,我们可以指定特定的动画,例如 snappy ,针对不同阶段,而不是使用默认动画。

使用触发器

目前,阶段动画器会自动启动动画并无限重复。但是,在某些情况下,您可能希望手动触发动画。在这种情况下,您可以通过在 trigger 相位动画器的参数。

例如,当用户点击表情符号时,应触发表情符号动画。您可以先声明一个状态变量,如下所示:

@State private var startAnimation = false

接下来,更新 phaseAnimator 通过添加修饰符 trigger 范围:

.phaseAnimator(Phase.allCases, trigger: startAnimation, content: { content, phase in
   content
      .scaleEffect(phase.scale)
      .rotationEffect(phase.angle)
      .offset(y: phase.offset)
}, animation: { phase in
   switch phase {
   case .initial: .bouncy
   case .rotate: .smooth
   case .jump: .snappy
   case .fall: .interactiveSpring
  }
})

修改代码后,动画只会在以下情况下触发: startAnimationfalsetrue 。为此,请将 onTapGesture 文本视图的修饰符。

.onTapGesture {
   startAnimation.toggle()
}

当用户点击表情符号时,我们会切换 startAnimation .这将触发多步骤动画。

概括

引入 PhaseAnimator 使创建多步骤动画的过程变得非常简单。通过使用枚举来定义动画的每个步骤应发生哪些变化,您只需几行代码即可创建动态且引人入胜的动画。SwiftUI 的 PhaseAnimator 以及其他有用的功能,为您完成艰苦的工作,因此开发人员可以专注于制作令人印象深刻的动画而无任何麻烦。

如果您喜欢阅读本教程,您可以继续阅读我们的《掌握 SwiftUI》一书,以了解有关 SwiftUI 框架的更多信息。