显性动画
只需在需要变化的组件键入animation 我们就能拥有基础的动画
struct ContentView: View {
@State private var circleColorChanged = false
@State private var heartColorChanged = false
@State private var heartSizeChanged = false
var body: some View {
ZStack {
Circle()
.frame(width: 200, height: 200)
.foregroundColor(circleColorChanged ? Color(.systemGray5) : .red)
Image(systemName: "heart.fill")
.foregroundColor(heartColorChanged ? .red : .white)
.font(.system(size: 100))
.scaleEffect(heartSizeChanged ? 1.0 : 0.5)
}
.animation(.default, value: circleColorChanged)
.onTapGesture {
self.circleColorChanged.toggle()
self.heartColorChanged.toggle()
self.heartSizeChanged.toggle()
}
}
}
自定义动画
SwiftUI 提供linear、easeIn、easeOut、easeInOut 与spring等动画方式
如果上面的动画效果我们成下面的动画 就会产生心跳的动画
.animation(.spring(response: 0.3, dampingFraction: 0.3, blendDuration: 0.3), value: circleColorChanged)
效果如下
隐性动画
只需将状态变化包裹在withAnimation 中 就可以做到相同的动画
struct ContentView: View {
@State private var circleColorChanged = false
@State private var heartColorChanged = false
@State private var heartSizeChanged = false
var body: some View {
ZStack {
Circle()
.frame(width: 200, height: 200)
.foregroundColor(circleColorChanged ? Color(.systemGray5) : .red)
Image(systemName: "heart.fill")
.foregroundColor(heartColorChanged ? .red : .white)
.font(.system(size: 100))
.scaleEffect(heartSizeChanged ? 1.0 : 0.5)
}
.onTapGesture {
withAnimation(.spring(response: 0.3, dampingFraction: 0.3, blendDuration: 0.3)) {
self.circleColorChanged.toggle()
self.heartColorChanged.toggle()
self.heartSizeChanged.toggle()
}
}
}
}
注: 如果某一种动画不需要 可以直接移到withAnimation 外即可 例如:心形大小不要
.onTapGesture {
withAnimation(.spring(response: 0.3, dampingFraction: 0.3, blendDuration: 0.3)) {
self.circleColorChanged.toggle()
self.heartColorChanged.toggle()
}
self.heartSizeChanged.toggle()
}
RotationEffect 实现下载进度条
基础用法
Circle()
.trim(from: 0, to: 0.9)
.stroke(Color.green, lineWidth: 5)
.frame(width: 100, height: 100)
.rotationEffect(Angle(degrees: isLoading ? 360 : 0))
.animation(.default.repeatForever(autoreverses: false), value: isLoading)
//出现在画面上
.onAppear() {
self.isLoading = true
}
嵌套使用圆形
ZStack{
Circle()
.trim(from: 0, to: 1)
.stroke(Color(.systemGray5), lineWidth: 20)
.frame(width: 100, height: 100)
Circle()
.trim(from: 0, to: 0.3)
.stroke(Color.green, lineWidth: 10)
.frame(width: 100, height: 100)
.rotationEffect(Angle(degrees: isLoading ? 360 : 0))
.animation(.linear(duration: 3).repeatForever(autoreverses: false), value: isLoading)
}
//出现在画面上
.onAppear() {
self.isLoading = true
}
直线进度条
RoundedRectangle 圆角矩形
ZStack {
Text("Loading")
.font(.system(.body, design: .rounded))
.bold()
.offset(x: 0, y: -25)
RoundedRectangle(cornerRadius: 3)
.stroke(Color(.systemGray5), lineWidth: 10)
.frame(width: 250, height: 3)
RoundedRectangle(cornerRadius: 3)
.stroke(Color.green, lineWidth: 10)
.frame(width: 30, height: 3)
.offset(x: isLoading ? 110 : -110, y: 0)
.animation(.linear(duration: 1).repeatForever(autoreverses: false), value: isLoading)
}
.onAppear() {
self.isLoading = true
}
使用Timer 更新下载进度
struct ContentView: View {
@State private var progress = 0.0
var body: some View {
ZStack{
Text("\(Int(progress * 100))%")
.font(.system(.title, design: .rounded))
.bold()
Circle()
.trim(from: 0, to: 1)
.stroke(Color(.systemGray5), lineWidth: 20)
.frame(width: 200, height: 200)
Circle()
.trim(from: 0, to: progress)
.stroke(Color.green, lineWidth: 20)
.frame(width: 200, height: 200)
.rotationEffect(Angle(degrees: -90))
}
//出现在画面上
.onTapGesture {
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in
self.progress += 0.05
if self.progress >= 1.0 {
timer.invalidate()
}
}
}
}
}