SwiftUI案例:自定义加载动画

2022-08-05 19:21:57 浏览数 (1)

SwiftUI案例:自定义加载动画

效果

目标

  • 使用图片实现自定义加载动画
  • 点击加载弹出层外部任意一点可关闭动画演示
  • 开关可控制图片采用“垂直移动动画”或“垂直加自身旋转动画”

外观配置

视图的实现

ContentView.swift

案例通过在间隔时间内不断控制变量 animateBall:BoolanimateRotation:Bool 的值来间接地实现动画效果;

代码语言:javascript复制
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

0 人点赞