|
|
import SwiftUI
|
|
|
|
|
|
struct LaunchScreenView: View {
|
|
|
@State private var isAnimating = false
|
|
|
@State private var iconScale: CGFloat = 0.8
|
|
|
@State private var textOpacity: Double = 0.0
|
|
|
@State private var backgroundOpacity: Double = 0.0
|
|
|
@State private var showNetworkPermission = false
|
|
|
@StateObject private var networkManager = NetworkPermissionManager()
|
|
|
|
|
|
let onLaunchComplete: () -> Void
|
|
|
|
|
|
var body: some View {
|
|
|
ZStack {
|
|
|
// 背景渐变 - 更专业的配色
|
|
|
LinearGradient(
|
|
|
gradient: Gradient(colors: [
|
|
|
Color(red: 0.05, green: 0.1, blue: 0.25),
|
|
|
Color(red: 0.15, green: 0.25, blue: 0.5),
|
|
|
Color(red: 0.25, green: 0.35, blue: 0.7)
|
|
|
]),
|
|
|
startPoint: .topLeading,
|
|
|
endPoint: .bottomTrailing
|
|
|
)
|
|
|
.ignoresSafeArea()
|
|
|
.opacity(backgroundOpacity)
|
|
|
|
|
|
// 网络权限检查视图
|
|
|
if showNetworkPermission {
|
|
|
NetworkPermissionView(networkManager: networkManager) {
|
|
|
// 网络权限获取成功,继续启动
|
|
|
showNetworkPermission = false
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
|
|
|
onLaunchComplete()
|
|
|
}
|
|
|
}
|
|
|
.transition(.opacity.combined(with: .scale))
|
|
|
} else {
|
|
|
// 正常的启动界面
|
|
|
launchContentView
|
|
|
.opacity(showNetworkPermission ? 0 : 1)
|
|
|
}
|
|
|
}
|
|
|
.onAppear {
|
|
|
startAnimations()
|
|
|
// 延迟检查网络权限
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
|
|
|
checkNetworkPermission()
|
|
|
}
|
|
|
}
|
|
|
.onChange(of: networkManager.isNetworkAvailable) { isAvailable in
|
|
|
if !isAvailable && !networkManager.isCheckingNetwork {
|
|
|
showNetworkPermission = true
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private var launchContentView: some View {
|
|
|
ZStack {
|
|
|
// 动态背景粒子效果
|
|
|
ForEach(0..<20) { index in
|
|
|
Circle()
|
|
|
.fill(Color.white.opacity(0.1))
|
|
|
.frame(width: CGFloat.random(in: 2...6))
|
|
|
.position(
|
|
|
x: CGFloat.random(in: 0...UIScreen.main.bounds.width),
|
|
|
y: CGFloat.random(in: 0...UIScreen.main.bounds.height)
|
|
|
)
|
|
|
.scaleEffect(isAnimating ? 1.5 : 0.5)
|
|
|
.opacity(isAnimating ? 0.3 : 0.0)
|
|
|
.animation(
|
|
|
.easeInOut(duration: Double.random(in: 2...4))
|
|
|
.repeatForever(autoreverses: true)
|
|
|
.delay(Double(index) * 0.1),
|
|
|
value: isAnimating
|
|
|
)
|
|
|
}
|
|
|
|
|
|
VStack(spacing: 40) {
|
|
|
Spacer()
|
|
|
|
|
|
// 应用图标区域
|
|
|
VStack(spacing: 25) {
|
|
|
// 应用图标容器
|
|
|
ZStack {
|
|
|
// 背景光晕效果
|
|
|
Circle()
|
|
|
.fill(
|
|
|
RadialGradient(
|
|
|
gradient: Gradient(colors: [
|
|
|
Color.white.opacity(0.4),
|
|
|
Color.white.opacity(0.1),
|
|
|
Color.clear
|
|
|
]),
|
|
|
center: .center,
|
|
|
startRadius: 30,
|
|
|
endRadius: 100
|
|
|
)
|
|
|
)
|
|
|
.frame(width: 180, height: 180)
|
|
|
.scaleEffect(isAnimating ? 1.3 : 0.8)
|
|
|
.opacity(isAnimating ? 0.7 : 0.0)
|
|
|
.animation(.easeInOut(duration: 2.5).repeatForever(autoreverses: true), value: isAnimating)
|
|
|
|
|
|
// 主图标容器
|
|
|
RoundedRectangle(cornerRadius: 35)
|
|
|
.fill(
|
|
|
LinearGradient(
|
|
|
gradient: Gradient(colors: [
|
|
|
Color.white.opacity(0.95),
|
|
|
Color.white.opacity(0.8)
|
|
|
]),
|
|
|
startPoint: .topLeading,
|
|
|
endPoint: .bottomTrailing
|
|
|
)
|
|
|
)
|
|
|
.frame(width: 140, height: 140)
|
|
|
.shadow(color: .black.opacity(0.4), radius: 25, x: 0, y: 15)
|
|
|
.scaleEffect(iconScale)
|
|
|
.animation(.spring(response: 1.0, dampingFraction: 0.7), value: iconScale)
|
|
|
|
|
|
// 应用图标 - 使用系统图标作为主要图标
|
|
|
Image(systemName: "qrcode.viewfinder")
|
|
|
.font(.system(size: 60, weight: .medium))
|
|
|
.foregroundStyle(
|
|
|
LinearGradient(
|
|
|
gradient: Gradient(colors: [
|
|
|
Color.blue,
|
|
|
Color.purple
|
|
|
]),
|
|
|
startPoint: .topLeading,
|
|
|
endPoint: .bottomTrailing
|
|
|
)
|
|
|
)
|
|
|
.frame(width: 90, height: 90)
|
|
|
.background(
|
|
|
RoundedRectangle(cornerRadius: 20)
|
|
|
.fill(Color.white.opacity(0.9))
|
|
|
)
|
|
|
.clipShape(RoundedRectangle(cornerRadius: 20))
|
|
|
.shadow(color: .black.opacity(0.2), radius: 10, x: 0, y: 5)
|
|
|
.scaleEffect(isAnimating ? 1.1 : 1.0)
|
|
|
.animation(.easeInOut(duration: 2.0).repeatForever(autoreverses: true), value: isAnimating)
|
|
|
} // 关闭ZStack
|
|
|
|
|
|
// 应用名称和描述
|
|
|
VStack(spacing: 15) {
|
|
|
|
|
|
Text("app_subtitle".localized)
|
|
|
.font(.system(size: 20, weight: .medium))
|
|
|
.foregroundColor(.white.opacity(0.9))
|
|
|
.multilineTextAlignment(.center)
|
|
|
.shadow(color: .black.opacity(0.3), radius: 2, x: 0, y: 1)
|
|
|
.onAppear {
|
|
|
#if DEBUG
|
|
|
print("🚀 LaunchScreen - app_subtitle: \("app_subtitle".localized)")
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
Text("app_description_launch".localized)
|
|
|
.font(.system(size: 16, weight: .regular))
|
|
|
.foregroundColor(.white.opacity(0.7))
|
|
|
.multilineTextAlignment(.center)
|
|
|
.shadow(color: .black.opacity(0.2), radius: 1, x: 0, y: 1)
|
|
|
.onAppear {
|
|
|
#if DEBUG
|
|
|
print("🚀 LaunchScreen - app_description_launch: \("app_description_launch".localized)")
|
|
|
#endif
|
|
|
}
|
|
|
}
|
|
|
.opacity(textOpacity)
|
|
|
}
|
|
|
|
|
|
Spacer()
|
|
|
|
|
|
// 加载指示器
|
|
|
VStack(spacing: 20) {
|
|
|
// 进度条
|
|
|
RoundedRectangle(cornerRadius: 3)
|
|
|
.fill(Color.white.opacity(0.2))
|
|
|
.frame(width: 250, height: 6)
|
|
|
.overlay(
|
|
|
RoundedRectangle(cornerRadius: 3)
|
|
|
.fill(
|
|
|
LinearGradient(
|
|
|
gradient: Gradient(colors: [
|
|
|
Color.blue,
|
|
|
Color.purple
|
|
|
]),
|
|
|
startPoint: .leading,
|
|
|
endPoint: .trailing
|
|
|
)
|
|
|
)
|
|
|
.frame(width: 250 * (isAnimating ? 1.0 : 0.0), height: 6)
|
|
|
.animation(.easeInOut(duration: 2.5), value: isAnimating)
|
|
|
)
|
|
|
|
|
|
// 加载状态文本
|
|
|
Text("initializing".localized)
|
|
|
.font(.system(size: 16, weight: .medium))
|
|
|
.foregroundColor(.white.opacity(0.8))
|
|
|
.opacity(textOpacity)
|
|
|
.onAppear {
|
|
|
#if DEBUG
|
|
|
print("🚀 LaunchScreen - initializing: \("initializing".localized)")
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
// 加载点
|
|
|
HStack(spacing: 15) {
|
|
|
ForEach(0..<3) { index in
|
|
|
Circle()
|
|
|
.fill(Color.white)
|
|
|
.frame(width: 12, height: 12)
|
|
|
.scaleEffect(isAnimating ? 1.3 : 0.8)
|
|
|
.opacity(isAnimating ? 1.0 : 0.4)
|
|
|
.animation(
|
|
|
.easeInOut(duration: 1.0)
|
|
|
.repeatForever(autoreverses: true)
|
|
|
.delay(Double(index) * 0.25),
|
|
|
value: isAnimating
|
|
|
)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
.opacity(textOpacity)
|
|
|
|
|
|
Spacer()
|
|
|
}
|
|
|
.padding(.horizontal, 40)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private func checkNetworkPermission() {
|
|
|
// 如果网络不可用,显示权限请求界面
|
|
|
if !networkManager.isNetworkAvailable {
|
|
|
showNetworkPermission = true
|
|
|
} else {
|
|
|
// 网络可用,继续启动流程
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
|
|
|
onLaunchComplete()
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private func startAnimations() {
|
|
|
// 启动背景动画
|
|
|
withAnimation(.easeIn(duration: 1.0)) {
|
|
|
backgroundOpacity = 1.0
|
|
|
}
|
|
|
|
|
|
// 启动图标动画
|
|
|
withAnimation(.spring(response: 1.0, dampingFraction: 0.7).delay(0.2)) {
|
|
|
iconScale = 1.0
|
|
|
}
|
|
|
|
|
|
// 启动文本动画
|
|
|
withAnimation(.easeIn(duration: 1.0).delay(0.5)) {
|
|
|
textOpacity = 1.0
|
|
|
}
|
|
|
|
|
|
// 启动循环动画
|
|
|
withAnimation(.easeIn(duration: 0.1).delay(0.8)) {
|
|
|
isAnimating = true
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#Preview {
|
|
|
LaunchScreenView {
|
|
|
print("Launch completed")
|
|
|
}
|
|
|
}
|