SwiftUI小功能模块系列
0001、SwiftUI自定义Tabbar动画效果
0002、SwiftUI自定义3D动画导航抽屉效果
0003、SwiftUI搭建瀑布流-交错网格-效果
0004、SwiftUI-<探探App>喜欢手势卡片
0005、SwiftUI-粘性动画指示器引导页
0006、SwiftUI自定义引导页动画
0007、SwiftUI聚光灯介绍说明
0008、SwiftUI-自定义启动闪屏动画-App启动闪屏曲线路径动画
技术:SwiftUI、SwiftUI3.0、动画闪屏、启动动画闪屏、启动闪屏曲线路径动画、动画灵感 - 来自德国的
freYfahrt
打车软件
运行环境:
SwiftUI3.0 + Xcode13.4.1 + MacOS12.5 + iPhone Simulator iPhone 13 Pro Max
使用SwiftUI做一个App启动闪屏logo定制曲线路径的案例
思路:
1.创建闪屏动画页面
2.画图 画出J 、| 、和J的倒放
、2个小圆点
然后控制动画
3.创建 主页导航 + 滚动视图 + N个图片
4.处理闪屏动画结束 展示主页逻辑
SplashsAnimation
颜色
SplashColor#DD8482
随机图片5张
New Group
命名为 View
New File
选择SwiftUI View
类型 命名为SplashScreen
New File
选择SwiftUI View
类型 命名为Home
主要是:
展示
闪屏过后的首页
主要是展示主窗口
Home
和闪屏页面
一开始是闪屏页面 。闪屏动画执行完毕 就会偏移y为整个屏幕的高度。
所以首页需要判断是否动画结束完成 完成就进行一个偏移
//
// ContentView.swift
// Shared
//
// Created by 李宇鸿 on 2022/8/19.
//
import SwiftUI
struct ContentView: View {
// 当动画完成时做动作…
@State var endAnimation : Bool = false
var body: some View {
ZStack {
Home()
// 一开始将首页y设置为0 动画过渡完毕 就偏移到整个屏幕的高度
.offset(y:endAnimation ? 0 : getRect().height)
SplashScreen(endAnimation:$endAnimation)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
思路
- 主要就是展示一个导航栏 + 滚动视图 + 多张图片
//
// Home.swift
// SplashsAnimation (iOS)
//
// Created by 李宇鸿 on 2022/8/19.
//
import SwiftUI
struct Home: View {
var body: some View {
NavigationView{
ScrollView(.vertical,showsIndicators: false) {
VStack(spacing:20){
ForEach(1...5,id:\.self){index in
Image("Pic\(index)")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: getRect().width - 30, height: 220)
.cornerRadius(15)
}
}
.padding(15)
}
.navigationTitle("Trending Posts")
}
}
}
struct Home_Previews: PreviewProvider {
static var previews: some View {
Home()
}
}
做法思路
- 画出几个图形
J 、| 、和J的倒放
、2个小圆点
- 通过逻辑去控制每一个的动画 - 注意其中
J 、| 、和J的倒放
是一个动画组- 控制动画结束之后 展示首页
//
// SplashScreen.swift
// SplashsAnimation (iOS)
//
// Created by 李宇鸿 on 2022/8/19.
//
import SwiftUI
struct SplashScreen: View {
// 动画属性……
@State var startAnimation : Bool = false
@State var circleAnimation1 : Bool = false
@State var circleAnimation2 : Bool = false
// 结束的动画
@Binding var endAnimation : Bool
var body: some View {
ZStack{
Color("SplashColor")
Group{
// 自定义形状与动画…
SplashShape()
// 修剪
.trim(from: 0, to: startAnimation ? 1 : 0)
// 描边得到轮廓
.stroke(Color.white,style: StrokeStyle(lineWidth: 30, lineCap: .round, lineJoin: .round))
// 两个圆……
Circle()
.fill(.white)
.frame(width: 35, height: 35)
.scaleEffect(circleAnimation1 ? 1 : 0)
.offset(x:-80, y: 22)
Circle()
.fill(.white)
.frame(width: 35, height: 35)
.scaleEffect(circleAnimation2 ? 1 : 0)
.offset(x:80, y: -22)
}
// 默认尺寸
.frame(width:220,height:130)
// 如果是动画已经结束 就缩放到0.15倍
.scaleEffect(endAnimation ? 0.15 : 0.9)
.rotationEffect(.init(degrees: endAnimation ? 85: 0))
// 底部文案
VStack{
Text("Powered by")
.font(.callout)
.fontWeight(.semibold)
Text("宇夜iOS")
.font(.title2)
.fontWeight(.semibold)
}
.frame(maxHeight:.infinity,alignment: .bottom)
.foregroundColor(Color.white.opacity(0.8))
.padding(.bottom,getSafeArea().bottom == 0 ? 15 : getSafeArea().bottom)
.opacity(startAnimation ? 1 : 0)
.opacity(endAnimation ? 0 : 1)
}
// 移动视图…
.offset(y:endAnimation ? (-getRect().height * 1.5) : 0)
.ignoresSafeArea()
.onAppear {
// 推迟开始……
withAnimation(.spring().delay(0.15)){
// 第一圈……
circleAnimation1.toggle()
}
// 下一个形状……
withAnimation(.interactiveSpring(response: 0.7, dampingFraction: 1.05, blendDuration: 1.05).delay(0.3)){
startAnimation.toggle()
}
// 最后一个圆
withAnimation(.spring().delay(0.7)){
circleAnimation2.toggle()
}
// 动画结束
withAnimation(.interactiveSpring(response: 0.7, dampingFraction: 1.05, blendDuration: 1.05).delay(1.2)){
endAnimation.toggle()
}
}
}
}
struct SplashScreen_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
// 扩展视图以获得屏幕框架…
extension View{
func getRect()->CGRect{
return UIScreen.main.bounds
}
// 安全的地方
func getSafeArea()->UIEdgeInsets{
guard let screen = UIApplication.shared.connectedScenes.first as? UIWindowScene else{
return .zero
}
guard let safeArea = screen.windows.first?.safeAreaInsets else{
return .zero
}
return safeArea
}
}
struct SplashShape: Shape {
// 画出3个路径 J | J的反转
func path(in rect: CGRect) -> Path {
return Path{ path in
let mid = rect.width / 2
let height = rect.height
// 80 = 40:弧半径…
path.move(to: CGPoint(x: mid - 80, y:height))
path.addArc(center: CGPoint(x: mid - 40 , y: height), radius: 40, startAngle: .init(degrees: 180), endAngle: .zero, clockwise: true)
// 直线……
path.move(to: CGPoint(x: mid, y: height))
path.addLine(to: CGPoint(x: mid, y: 0))
// 另一个弧…
path.addArc(center: CGPoint(x: mid + 40 , y: 0), radius: 40, startAngle: .init(degrees: -180), endAngle: .zero, clockwise: false)
}
}
}
如需看源码,请点击下载!