Pow

为你的 App 带来令人愉悦的 SwiftUI 效果。

查看来自 Emerge Tools 的其他开源项目

安装

要将软件包依赖项添加到你的 Xcode 项目中,请选择File > Add Package,然后输入此存储库的 URL (https://github.com/EmergeTools/Pow)。

要将软件包依赖项添加到 Swift Package,请将此存储库添加到你的依赖项列表中。

.package(url: "https://github.com/EmergeTools/Pow", from: Version(1, 0, 0))

并将它作为 product 添加到你的 target 中

.product(name: "Pow", package: "Pow")

如果你要从之前闭源的 Pow 框架迁移到新的开源包,请参考我们的迁移指南。如果遇到任何问题,请提交 issue

概述

Pow 包含精选的 SwiftUI 转场动画以及每次值更新时触发的变化效果

你可以在 Pow 网站上找到所有效果的预览。如果你有 iOS 开发者环境,可以查看 Pow 示例 App

反馈 & 贡献

此项目提供了多种向维护者提供反馈的形式。

如果你正在研究如何使用 Pow 或其某个效果,我们建议你首先查阅效果示例页面

如果你仍然有疑问、改进建议或改进 Pow 的方法,此项目利用 GitHub 的 Issues 来管理你的请求。如果你发现错误并希望报告它,我们将非常感谢你提交 issue。

要求

变化效果

变化效果是指每次值发生变化时都会触发视觉或触觉效果的效果。

使用 changeEffect 修饰符,并传入 AnyChangeEffect 以及要监视更改的值。

Button {
    post.toggleLike()
} label: {
    Label(post.likes.formatted(), systemName: "heart.fill")
}
.changeEffect(.spray { heart }, value: post.likes, isEnabled: post.isLiked)
.tint(post.isLiked ? .red : .gray)

你可以从以下变化效果中进行选择:SprayHaptic FeedbackJumpPingRiseShakeShineSpin

Spray

预览

一种从原点向上发射不同阴影和大小的多个粒子的效果。

likeButton
  .changeEffect(
    .spray(origin: .center) { Image(systemName: "heart.fill") },
    value: likes
  )
static func spray(origin: UnitPoint = .center, layer: ParticleLayer = .local, @ViewBuilder _ particles: () -> some View) -> AnyChangeEffect

Haptic 反馈

每当值发生变化时,触发触觉反馈以传达成功、失败和警告。

static func feedback(hapticNotification type: UINotificationFeedbackGenerator.FeedbackType) -> AnyChangeEffect

每当值发生变化时,触发触觉反馈以模拟物理冲击。

static func feedback(hapticImpact style: UIImpactFeedbackGenerator.FeedbackStyle) -> AnyChangeEffect

每当值发生变化时,触发触觉反馈以指示选择的变化。

static var feedbackHapticSelection: AnyChangeEffect

Jump

预览

使视图跳跃给定的高度,然后弹跳几次才稳定下来。

static func jump(height: CGFloat) -> AnyChangeEffect

Ping

预览

在视图后面添加一个或多个缓慢增长和淡出的形状。

该形状将由当前的色调样式着色。

  static func ping(shape: some InsettableShape, count: Int) -> AnyChangeEffect

一种在视图后面添加一个或多个缓慢增长和淡出的形状的效果。

static func ping(shape: some InsettableShape, style: some ShapeStyle, count: Int) -> AnyChangeEffect

Rise

预览

一种从原点发射提供的粒子,并在左右移动的同时缓慢向上漂浮的效果。

static func rise(origin: UnitPoint = .center, layer: ParticleLayer = .local, @ViewBuilder _ particles: () -> some View) -> AnyChangeEffect

Shake

预览

当发生变化时,摇动视图。

static var shake: AnyChangeEffect

一种当发生变化时摇动视图的效果。

static func shake(rate: ShakeRate) -> AnyChangeEffect

Shine

预览

用在视图上移动的光泽突出显示视图。

光泽从顶部前沿移动到底部后沿。

static var shine: AnyChangeEffect

用在视图上移动的光泽突出显示视图。

光泽从顶部前沿移动到底部后沿。

static func shine(duration: Double) -> AnyChangeEffect

用在视图上移动的光泽突出显示视图。

角度相对于当前的 layoutDirection,因此 0° 表示向后沿扫描,90° 表示向底部边缘扫描。

static func shine(angle: Angle, duration: Double = 1.0) -> AnyChangeEffect

声音效果反馈

每当值发生变化时,触发声音效果作为反馈。

此效果不会中断或降低当前正在播放的任何其他音频。不能保证此效果会被触发;效果的运行取决于用户的静音开关位置和当前的播放设备。

为了向用户传递重要信息,你应该始终将音频效果与视觉提示结合使用。

static func feedback(_ soundEffect: SoundEffect) -> AnyChangeEffect

Spin

预览

当发生变化时,围绕给定轴旋转视图。

static var spin: AnyChangeEffect

当发生变化时,围绕给定轴旋转视图。

static func spin(axis: (x: CGFloat, y: CGFloat, z: CGFloat), anchor: UnitPoint = .center, anchorZ: CGFloat = 0, perspective: CGFloat = 1 / 6) -> AnyChangeEffect

延迟

每个变化效果都可以延迟,以便在一段时间后触发效果。

Button("Submit") { 
    <#code#>
}
.buttonStyle(.borderedProminent)
.disabled(name.isEmpty)
.changeEffect(.shine.delay(1), value: name.isEmpty, isEnabled: !name.isEmpty)
func delay(_ delay: Double) -> AnyChangeEffect

粒子图层

粒子图层是粒子效果绘制其粒子的上下文。

particleLayer(name:) 视图修饰符将视图包装在具有给定名称的粒子图层中。

粒子效果(如 AnyChangeEffect.spray)可以在视图树中的此位置渲染其粒子,以避免被其直接祖先裁剪。

例如,某些 List 样式可能会裁剪其行。使用 particleLayer(_:) 在整个 List 甚至其封闭的 NavigationStack 上方渲染粒子。

func particleLayer(name: AnyHashable) -> some View

转场动画

所有转场动画都在 movingParts 静态变量下命名空间,例如:

myView.transition(.movingParts.anvil)

Anvil

预览

一种从顶部向下掉落视图并带有匹配的触觉反馈的转场动画。

该转场动画仅在插入时执行,需要 1.4 秒。

static var anvil: AnyTransition

Blinds

预览

一种以百叶窗的形式显示视图的转场动画。

static var blinds: AnyTransition

一种以百叶窗的形式显示视图的转场动画。

参数

static func blinds(slatWidth: CGFloat, style: BlindsStyle = .venetian, isStaggered: Bool = false) -> AnyTransition

Blur

预览

一种在插入时从模糊到清晰,在移除时从清晰到模糊的转场动画。

static var blur: AnyTransition

Boing

预览

一种将视图向下移动的转场动画,任何超调都会导致视图的弹性变形。

static var boing: AnyTransition

一种从指定的边缘移动视图以进行插入的转场动画,
并在移除时朝向它移动,任何超调都会导致弹性的
视图的变形。

static func boing(edge: Edge) -> AnyTransition

Clock

预览

一种使用围绕视图中心点的顺时针扫描的转场动画。

static var clock: AnyTransition

一种使用围绕视图中心点的顺时针扫描的转场动画。

static func clock(blurRadius: CGFloat)  -> AnyTransition

Flicker

预览

一种在稳定之前多次切换视图可见性的转场动画。

static var flicker: AnyTransition

一种在稳定之前多次切换视图可见性的转场动画。

static func flicker(count: Int) -> AnyTransition

Film Exposure

预览

一种在插入时从完全黑暗到完全可见,在移除时从完全可见到完全黑暗的转场动画。

static var filmExposure: AnyTransition

Flip

预览

一种通过将视图旋转朝向查看者来插入,并通过将视图旋转远离查看者来移除的转场动画。

注意:动画的任何超调都会导致视图继续旋转超过视图的正常状态,然后最终稳定下来。

static var flip: AnyTransition

Glare

预览

一种通过将对角线擦拭与白色条纹结合来显示视图的转场动画。

static var glare: AnyTransition

一种通过将擦拭与彩色条纹结合来显示视图的转场动画。

角度相对于当前的 layoutDirection,因此 0° 表示在插入时向后沿扫描,90° 表示向底部边缘扫描。

在此示例中,视图的移除使用具有指数缓入曲线的光晕,并结合预期的缩放动画,从而实现更具戏剧性的退出。

infoBox
  .transition(
    .asymmetric(
      insertion: .movingParts.glare(angle: .degrees(225)),
      removal: .movingParts.glare(angle: .degrees(45)
    )
    .animation(.movingParts.easeInExponential(duration: 0.9))
        .combined(with:
          .scale(scale: 1.4)
            .animation(.movingParts.anticipate(duration: 0.9).delay(0.1)
        )
      )
    )
  )
static func glare(angle: Angle, color: Color = .white) -> AnyTransition

Iris

预览

一种在插入时呈现为增长的圆形,在移除时呈现为收缩的圆形的转场动画。

static func iris(origin: UnitPoint = .center, blurRadius: CGFloat = 0) -> AnyTransition

Move

预览

一种从插入时视图的指定边缘移动视图,并在移除时朝向它的转场动画。

static func move(edge: Edge) -> AnyTransition

一种以指定的角度移动视图的转场动画。

角度相对于当前的 layoutDirection,因此 0° 表示在插入时向后沿动画,90° 表示向底部边缘插入。

在此示例中,视图插入通过将其移动到顶部后沿角来动画,而移除通过将其移动到底部边缘来动画。

Text("Hello")
  .transition(
    .asymmetric(
      insertion: .movingParts.move(angle: .degrees(45)),
      removal:   .movingParts.move(angle: .degrees(90))
    )
  )
static func move(angle: Angle) -> AnyTransition

Pop

预览

一种显示具有涟漪效果和大量彩色粒子的视图的转场动画。

该转场动画仅在插入时执行,需要 1.2 秒。

static var pop: AnyTransition

一种显示具有涟漪效果和大量彩色粒子的视图的转场动画。

在此示例中,星星仅在从 starred == false 过渡到 starred == true 时才使用 pop 效果

Button {
  starred.toggle()
} label: {
  if starred {
    Image(systemName: "star.fill")
      .foregroundStyle(.orange)
      .transition(.movingParts.pop(.orange))
  } else {
    Image(systemName: "star")
      .foregroundStyle(.gray)
      .transition(.identity)
  }
}

该转场动画仅在插入时执行。

static func pop<S: ShapeStyle>(_ style: S) -> AnyTransition

Poof

预览

一种以溶解的卡通风格云移除视图的转场动画。

该转场动画仅在移除时执行,需要 0.4 秒。

static var poof: AnyTransition

Rotate3D

一种通过从指定的旋转进行旋转来插入,并通过在三个维度中旋转到指定的旋转来移除的转场动画。

在此示例中,视图绕其底部边缘绕 y 轴旋转 90˚,就好像它是从其背面向上升起一样

Text("Hello")
  .transition(.movingParts.rotate3D(
    .degrees(90),
      axis: (1, 0, 0),
      anchor: .bottom,
      perspective: 1.0 / 6.0)
  )

注意:动画的任何超调都会导致视图继续旋转超过视图的正常状态,然后最终稳定下来。

static func rotate3D(_ angle: Angle, axis: (x: CGFloat, y: CGFloat, z: CGFloat), anchor: UnitPoint = .center, anchorZ: CGFloat = 0, perspective: CGFloat = 1) -> AnyTransition

Snapshot

预览

插入时从完全明亮过渡到完全可见,移除时从完全可见过渡到完全明亮。

static var snapshot: AnyTransition

侧滑 (Skid)

预览

一种过渡效果,将视图从其前缘滑入,任何过冲都会导致视图的弹性变形。

static var skid: AnyTransition

一种过渡效果,将视图从指定的边缘滑入(插入时),并向该边缘滑出(移除时),任何过冲都会导致视图的弹性变形。

static func skid(direction: SkidDirection) -> AnyTransition

嗖 (Swoosh)

预览

一种三维过渡效果,在插入时从后向前,在移除时从前向后。

static var swoosh: AnyTransition

消失 (Vanish)

预览

一种将视图溶解成许多小颗粒的过渡效果。

该过渡仅在移除时执行。

注意: 如果当前的 Animation.default,此过渡效果将使用持续时间为 900 毫秒的缓出 (ease-out) 动画。

static var vanish: AnyTransition

一种将视图溶解成许多小颗粒的过渡效果。

该过渡仅在移除时执行。

注意: 如果当前的 Animation.default,此过渡效果将使用持续时间为 900 毫秒的缓出 (ease-out) 动画。

static func vanish<S: ShapeStyle>(_ style: S) -> AnyTransition

一种将视图溶解成许多遵循给定形状的小颗粒的过渡效果。

该过渡仅在移除时执行。

注意: 如果当前的 Animation.default,此过渡效果将使用持续时间为 900 毫秒的缓出 (ease-out) 动画。

static func vanish<T: ShapeStyle, S: Shape>(_ style: T, mask: S, eoFill: Bool = false) -> AnyTransition

擦除 (Wipe)

预览

一种使用从指定边缘开始扫描(插入时)并向其扫描(移除时)的过渡效果。

static func wipe(edge: Edge, blurRadius: CGFloat = 0) -> AnyTransition

一种使用指定角度的扫描的过渡效果。

角度相对于当前的 layoutDirection,因此 0° 表示在插入时向后沿扫描,90° 表示向底部边缘扫描。

static func wipe(angle: Angle, blurRadius: CGFloat = 0) -> AnyTransition