SwiftUI案例:自定义加载动画
效果
目标
- 使用图片实现自定义加载动画
- 点击加载弹出层外部任意一点可关闭动画演示
- 开关可控制图片采用“垂直移动动画”或“垂直加自身旋转动画”
外观配置
视图的实现
ContentView.swift
案例通过在间隔时间内不断控制变量 animateBall:Bool
与 animateRotation:Bool
的值来间接地实现动画效果;
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
Home()
//使用Home()组件定义导航栏标题
.navigationTitle("弹跳的球动画")
}
}
}
//全局暴露ContentView()组件
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
//实例化当前页面容器
ContentView()
}
}
struct Home: View {
//rotateBall与showPopUp组成的flag标签组
@State var rotateBall = false //是否旋转图片
@State var showPopUp = false //是否显示弹出层
@Environment(.colorScheme) var scheme //使用预处理颜色组
var body: some View {
//创建纵向布局视图
VStack {
//"图片是否旋转"开关
Toggle(isOn: $rotateBall) {
Text("图片是否旋转")
}
.padding()
.padding(.horizontal, 10)
//弹出演示按钮
Button {
withAnimation(.spring()) {
showPopUp.toggle()
}
}
//设置按钮的label内容
label: {
Text("弹出演示")
.foregroundColor(.black)
.padding(.vertical, 10)
.padding(.horizontal, 25)
.background(scheme == .dark ? Color.black : Color.white)
.cornerRadius(8)
.shadow(color: Color.primary.opacity(0.1), radius: 5, x: 5, y: 5)
.shadow(color: Color.primary.opacity(0.1), radius: 5, x: -5, y: -5)
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.overlay(
//覆盖层
ZStack {
//如果"showPopUp"的flag是真则显示这个覆盖层
if showPopUp {
Color.primary.opacity(0.2)
.ignoresSafeArea()
.onTapGesture {
withAnimation(.spring()) {
showPopUp.toggle()
}
}
//调用"DribbleAnimatedView"视图组件
DribbleAnimatedView(showPopUp: $showPopUp, rotateBall: $rotateBall)
}
}
)
}
}
//视图DribbleAnimatedView加载组件
struct DribbleAnimatedView: View {
//颜色配置
@Environment(.colorScheme) var scheme
//需要函数调用时显式地传入这些flag组值
@Binding var showPopUp: Bool //使用全局showPopUp变量
@Binding var rotateBall: Bool //使用全局rotateBall变量
//动画绑定变量
@State var animateBall = false //是否显示动画
@State var animateRotation = false //是否旋转
var body: some View {
ZStack {
//调用全局scheme颜色配置并使用ZStack布局
(scheme == .dark ? Color.black : Color.white)
.frame(width: 150, height: 150)
.cornerRadius(14)
.shadow(color: Color.primary.opacity(0.1), radius: 5, x: 5, y: 5)
.shadow(color: Color.primary.opacity(0.1), radius: 5, x: -5, y: -5)
//动画球的底部阴影设置
Circle()
.fill(Color.gray.opacity(0.4))
//大小和尺寸使用animateBall:Bool动画的返回值来控制
.frame(width: animateBall ? 40 : 20, height: 40)
.rotation3DEffect(
.init(degrees: 70),
axis: (x: 1, y: 0, z: 0),
anchor: .center
)
.offset(y: 35)
//透明度使用animateBall:Bool动画的返回值来控制
.opacity(animateBall ? 1 : 0)
//使用图片"dirbble.jpeg"
Image("dirbble")
.resizable()
.clipShape(Circle())
.aspectRatio(contentMode: .fit)
.frame(width: 60, height: 60)
//旋转角度使用animateBall:Bool动画的返回值来控制
.rotationEffect(.init(degrees: rotateBall && animateRotation ? 360 : 0))
//位移偏量使用animateBall:Bool动画的返回值来控制
.offset(y: animateBall ? 10 : -25)
}
//显示的时候自动调用doAnimation()函数显示动画
.onAppear {
doAnimation()
}
}
//doAnimation()动画控制组件
func doAnimation(){
withAnimation(Animation.easeInOut(duration:0.6).repeatForever(autoreverses: true)) {
//0.6秒内不断切换animateBall的Bool值
//控制纵向弹跳的动画
animateBall.toggle()
}
withAnimation(Animation.easeInOut(duration:0.8).repeatForever(autoreverses: true)) {
//0.8秒内不断切换animateRotation的Bool值
//控制旋转
animateRotation.toggle()
}
}
}
CenterLoadingAnimationUI.zip
来源:百度网盘 | 提取码:gw4l
CenterLoadingAnimationUI.zip
来源:蓝奏云网盘 | 提取码:5uih