Animation

温馨提示:本文最后更新于2025-02-05 10:03:56,某些文章具有时效性,若有错误或已失效,请在下方留言

当指定的值发生变化时,将给定的动画应用于此视图。

struct ContentView: View {
    @State private var scale = 0.5

    var body: some View {
        VStack {
            Circle().scale(scale).animation(.easeIn, value: scale)
            HStack {
                Button("+") { scale += 0.1 }
                Button("-") { scale -= 0.1 }
            }
        }
    }
}

下面的代码用 easeInout 动画来显示不透明度的变化,而MyView的内容用隐含的事务动画来显示:

MyView(isActive: isActive)
    .animation(.easeInOut) { content in
        content.opacity(isActive ? 1.0 : 0.0)
    }

为绑定值添加动画

例如,以下代码中的视图执行线性动画,将箱型卡车在视图的前沿和后沿之间移动。每当用户点击切换控件时,即更改 $isTrailing 绑定的值时,卡车就会移动一次。

struct ContentView: View {
    @State private var isTrailing = false

    var body: some View {
       VStack(alignment: isTrailing ? .trailing : .leading) {
            Image(systemName: "box.truck").font(.system(size: 64))
            Toggle("Move to trailing edge", isOn: $isTrailing.animation(.linear))
        }
    }
}

配置动画

delay(TimeInterval)

将动画的开始延迟指定的秒数。

struct ContentView: View {
    @State private var adjustBy = 100.0

    var body: some View {
        VStack(spacing: 40) {
            HStack(alignment: .bottom) {
                Capsule()
                    .frame(width: 50, height: 175 - adjustBy)
                    .animation(.easeInOut, value: adjustBy)
                Capsule()
                    .frame(width: 50, height: 175 + adjustBy)
                    .animation(.easeInOut.delay(0.5), value: adjustBy)
            }
            Button("Animate") {
                adjustBy *= -1
            }
        }
    }
}

repeatCount(Int, autoreverses: Bool)

将动画重复指定次数。

struct ContentView: View {
    @State private var scale = 0.5
    var body: some View {
        VStack {
            Circle().scale(scale).animation(.easeIn.repeatCount(3), value: scale)
            HStack {
                Button("+") { scale += 0.1 }
                Button("-") { scale -= 0.1 }
            }
        }
    }
}

repeatForever(autoreverses: Bool)

在包含动画的视图的生命周期内重复动画。

struct ContentView: View {
    @State private var rotationDegrees = 0.0

    private var animation: Animation {
        .linear
        .speed(0.1)
        .repeatForever(autoreverses: false)
    }

    var body: some View {
        Image(systemName: "gear")
            .font(.system(size: 86))
            .rotationEffect(.degrees(rotationDegrees))
            .onAppear {
                withAnimation(animation) {
                    rotationDegrees = 360.0
                }
            }
    }
}

speed(Double)

通过调整动画的速度来改变动画的持续时间。

struct ContentView: View {
    @State private var adjustBy = 100.0

    private var oneSecondAnimation: Animation {
       .easeInOut(duration: 1.0)
    }

    var body: some View {
        VStack(spacing: 40) {
            HStack(alignment: .bottom) {
                Capsule().frame(width: 50, height: 175 - adjustBy)
                Capsule().frame(width: 50, height: 175 + adjustBy)
            }
            .animation(oneSecondAnimation.speed(2.0), value: adjustBy)

            Button("Animate") {
                adjustBy *= -1
            }
        }
    }
}

默认动画

default 一个默认的动画实例。

类型属性说明
linear以恒定速度移动的动画。
easeIn一个从开始缓慢启动然后向结束加快速度的动画。
easeOut一个从开始快速然后朝着结束减速的动画。
easeInOut一个结合了进入和退出缓动动画行为的动画。
bouncy具有预定义持续时间和较大弹跳量的弹簧动画。
smooth具有预定义持续时间和无弹跳的平滑弹簧动画。
snappy具有预定义持续时间和小幅弹跳的弹簧动画,感觉更灵活。

线性动画

.linear 以恒定速度移动的动画。

struct ContentView: View {
    @State private var isActive = false

    var body: some View {
        VStack(alignment: isActive ? .trailing : .leading) {
            Circle().fill(isActive ? Color.red : Color.blue).frame(width: 50, height: 50)
            Button("Animate") {
                withAnimation(.linear) {
                    isActive.toggle()
                }
            }
            .frame(maxWidth: .infinity)
        }
    }
}

linear(duration: TimeInterval) 一个在指定持续时间内以恒定速度移动的动画。

Button("Animate") {
  withAnimation(.linear(duration: 2.0)) {
      isActive.toggle()
  }
}

缓动动画

easeln

.easeIn一种动画,从开始时缓慢开始,然后朝着移动的结束加速。

struct ContentView: View {
    @State private var scale = 0.5
    var body: some View {
        VStack {
            Circle().scale(scale).animation(.easeIn, value: scale)
            HStack {
                Button("+") { scale += 0.1 }
                Button("-") { scale -= 0.1 }
            }
        }
    }
}

.easeIn(duration: TimeInterval) 一种具有指定持续时间的动画,开始时缓慢开始,然后朝着移动的结束加速。

.animation(.easeIn(duration: 1.0), value: scale)

easeOut

.easeOut 一种动画,开始时速度较快,然后朝着移动的结束减速。

struct ContentView: View {
    @State private var scale = 0.5
    var body: some View {
        VStack {
            Circle().scale(scale).animation(.easeOut, value: scale)
            HStack {
                Button("+") { scale += 0.1 }
                Button("-") { scale -= 0.1 }
            }
        }
    }
}

.easeOut(duration: TimeInterval)一种具有指定持续时间的动画,开始时速度较快,然后朝着移动的结束减速。

.animation(.easeOut(duration: 1.0), value: scale)

easelnOut

.easeInout 一种结合了入和出缓动动画行为的动画。

struct ContentView: View {
    @State private var scale = 0.5
    var body: some View {
        VStack {
            Circle().scale(scale).animation(.easeInOut, value: scale)
            HStack {
                Button("+") { scale += 0.1 }
                Button("-") { scale -= 0.1 }
            }
        }
    }
}

. easeInOut(duration: TimeInterval)是一个具有指定持续时间的动画,结合了入和出缓动动画的行为。

.animation(.easeInOut(duration: 1.0), value: scale)

内置弹簧动画

bouncy

.bouncy 具有预定义持续时间和较大弹跳量的弹簧动画。

.animation(.bouncy, value: scale)

bouncy(duration:TimeInterval,extraBounce:Double)具有预定义持续时间和更高弹跳量的弹簧动画,可进行调节。

.animation(.bouncy(duration: 0.5, extraBounce: 0.0), value: scale)

smooth

.smooth具有预定义持续时间且没有弹跳的平滑弹簧动画。

smooth(duration:TimeInterval,extraBounce:Double)具有预定义持续时间且没有弹簧的平滑弹簧动画,可进行调节。

snappy

.snappy 具有预定义持续时间且弹跳量较小的弹簧动画,感觉更加灵活。

snappy (duration:TimeInterval,extraBounce:Double)具有预定义持续时间且弹跳量较小的弹簧动画,感觉更加灵活且可调节。

创建自定义动画

创建一个包含指定自定义动画的 Animation。

struct ContentView: View {
    @State private var scale = 1.0

    var body: some View {
        VStack {
            Circle()
                .scaleEffect(scale)
                .animation(.timingCurve(0.1, 0.75, 0.85, 0.35, duration: 2.0), value: scale)

            Button("Animate") {
                if scale == 1.0 {
                    scale = 0.25
                } else {
                    scale = 1.0
                }
            }
        }
    }
}

扩展一个动画修饰符

创建一个即时动画

extension View {
    func animate(using animation: Animation = Animation.easeInOut(duration: 1), _ action: @escaping () -> Void) -> some View {
        onAppear {
            withAnimation(animation) {
                action()
            }
        }
    }
}
struct ContentView: View {
    @State var scale: CGFloat = 1

    var body: some View {
        Circle()
            .frame(width: 200, height: 200)
            .scaleEffect(scale)
            .animate({
                scale = 0.5
            })
    }
}

创建即时循环动画

extension View {
    func animateForever(using animation: Animation = Animation.easeInOut(duration: 1), autoreverses: Bool = false, _ action: @escaping () -> Void) -> some View {
        let repeated = animation.repeatForever(autoreverses: autoreverses)
        return onAppear {
            withAnimation(repeated) {
                action()
            }
        }
    }
}
struct AnimateForeverView: View {
    @State var scale: CGFloat = 1

    var body: some View {
        Circle()
            .frame(width: 200, height: 200)
            .scaleEffect(scale)
            .animateForever(autoreverses: true) { scale = 0.5 }
    }
}

interpolatingSpring

例如,这将创建一个具有适度弹簧阻尼的按钮,这意味着它将在达到目标角度之前来回弹跳几次:

struct ContentView: View {
    @State private var angle: Double = 0

    var body: some View {
        Button("Press here") {
            angle += 45
        }
        .rotationEffect(.degrees(angle))
        .animation(.interpolatingSpring(mass: 1, stiffness: 1, damping: 0.5, initialVelocity: 10))
    }
}

这是一个插补弹簧,这意味着如果多次触发动画,则弹簧组合时,弹簧效果将越来越强。

视图中应用多个动画

我们使用 SwiftUIanimation()修饰符的顺序会影响要对哪些修饰符进行动画处理,还可以添加多个 animation() 修饰符以获取不同的动画。

例如,我们可以编写代码来创建一个在启用状态和禁用状态之间进行动画处理的按钮,从而对圆角和背景颜色进行更改。如果我们希望为圆角动画而不是改变颜色,请在剪辑形状之后使用诸如 animation(.default) 之类的动画,然后在背景之后使用 animation(nil) 之类的动画:

struct ContentView: View {
    @State private var isEnabled = false

    var body: some View {
        Button("Press Me") {
            isEnabled.toggle()
        }
        .foregroundColor(.white)
        .frame(width: 200, height: 200)
        .background(isEnabled ? Color.green : Color.red)
        .animation(nil)
        .clipShape(RoundedRectangle(cornerRadius: isEnabled ? 100 : 0))
        .animation(.default)
    }
}

动画从一个视图同步到另一个视图

同一视图出现在视图层次结构的两个不同部分中,您将看到下面示例两个视图平滑移动:

struct ContentView: View {
    @Namespace private var animation
    @State private var isFlipped = false

    var body: some View {
        VStack {
            if isFlipped {
                Circle().fill(Color.red).frame(width: 44, height: 44)
                    .matchedGeometryEffect(id: "Shape", in: animation)

                Text("Taylor Swift – 1989").font(.headline)
                    .matchedGeometryEffect(id: "AlbumTitle", in: animation)
            } else {
                Text("Taylor Swift – 1989").font(.headline)
                    .matchedGeometryEffect(id: "AlbumTitle", in: animation)

                Circle().fill(Color.blue).frame(width: 44, height: 44)
                    .matchedGeometryEffect(id: "Shape", in: animation)
            }
        }
        .onTapGesture {
            withAnimation {
                isFlipped.toggle()
            }
        }
    }
}
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容