
- 注释掉Podfile中的Alamofire依赖,更新Podfile.lock以反映更改。 - 在yana/APIs/API-README.md中新增自动认证Header机制的详细文档,描述其工作原理、实现细节及最佳实践。 - 在yana/yanaApp.swift中将print语句替换为debugInfo以增强调试信息的输出。 - 在API相关文件中实现用户认证状态检查和相关header的自动添加逻辑,提升API请求的安全性和用户体验。 - 更新多个文件中的日志输出,确保在DEBUG模式下提供详细的调试信息。
227 lines
6.6 KiB
Swift
227 lines
6.6 KiB
Swift
import SwiftUI
|
||
|
||
// MARK: - API Loading Effect View
|
||
|
||
/// 全局 API 加载效果视图
|
||
///
|
||
/// 该视图显示在屏幕最顶层,包含:
|
||
/// - Loading 动画(88x88,60% alpha 黑色圆角背景)
|
||
/// - 错误信息显示(2秒后自动消失)
|
||
/// - 支持多个并发显示
|
||
/// - 不阻挡用户点击操作
|
||
struct APILoadingEffectView: View {
|
||
@ObservedObject private var loadingManager = APILoadingManager.shared
|
||
|
||
var body: some View {
|
||
ZStack {
|
||
// 🚨 极简渲染策略:避免复杂的 ForEach,只显示第一个需要显示的项目
|
||
if let firstItem = getFirstDisplayItem() {
|
||
SingleLoadingView(item: firstItem)
|
||
.onAppear {
|
||
debugInfo("🔍 Loading item appeared: \(firstItem.id)")
|
||
}
|
||
.onDisappear {
|
||
debugInfo("🔍 Loading item disappeared: \(firstItem.id)")
|
||
}
|
||
}
|
||
}
|
||
.allowsHitTesting(false) // 不阻挡用户点击
|
||
.ignoresSafeArea(.all) // 覆盖整个屏幕
|
||
.onReceive(loadingManager.$loadingItems) { items in
|
||
debugInfo("🔍 Loading items updated: \(items.count) items")
|
||
}
|
||
}
|
||
|
||
/// 安全地获取第一个需要显示的项目
|
||
private func getFirstDisplayItem() -> APILoadingItem? {
|
||
guard Thread.isMainThread else {
|
||
debugWarn("⚠️ getFirstDisplayItem called from background thread")
|
||
return nil
|
||
}
|
||
|
||
return loadingManager.loadingItems.first { $0.shouldDisplay }
|
||
}
|
||
}
|
||
|
||
// MARK: - Single Loading View
|
||
|
||
/// 单个加载项视图 - 极简版本
|
||
private struct SingleLoadingView: View {
|
||
let item: APILoadingItem
|
||
|
||
var body: some View {
|
||
Group {
|
||
switch item.state {
|
||
case .loading:
|
||
SimpleLoadingView()
|
||
|
||
case .error(let message):
|
||
if item.shouldShowError {
|
||
SimpleErrorView(message: message)
|
||
}
|
||
|
||
case .success:
|
||
EmptyView() // 成功状态不显示任何内容
|
||
}
|
||
}
|
||
// 🚨 移除复杂动画,避免渲染问题
|
||
}
|
||
}
|
||
|
||
// MARK: - Simple Loading View
|
||
|
||
/// 极简 Loading 视图
|
||
private struct SimpleLoadingView: View {
|
||
var body: some View {
|
||
VStack {
|
||
Spacer()
|
||
HStack {
|
||
Spacer()
|
||
|
||
// 极简黑色背景 + 白色圆圈
|
||
ZStack {
|
||
RoundedRectangle(cornerRadius: 12)
|
||
.fill(Color.black.opacity(0.6))
|
||
.frame(width: 88, height: 88)
|
||
|
||
// 使用最简单的 ProgressView
|
||
ProgressView()
|
||
.progressViewStyle(CircularProgressViewStyle(tint: .white))
|
||
.scaleEffect(1.2)
|
||
}
|
||
|
||
Spacer()
|
||
}
|
||
Spacer()
|
||
}
|
||
}
|
||
}
|
||
|
||
// MARK: - Simple Error View
|
||
|
||
/// 极简错误视图
|
||
private struct SimpleErrorView: View {
|
||
let message: String
|
||
|
||
var body: some View {
|
||
VStack {
|
||
Spacer()
|
||
HStack {
|
||
Spacer()
|
||
|
||
// 极简错误提示
|
||
VStack(spacing: 8) {
|
||
Image(systemName: "exclamationmark.triangle.fill")
|
||
.foregroundColor(.white)
|
||
.font(.title2)
|
||
|
||
Text(message)
|
||
.foregroundColor(.white)
|
||
.font(.system(size: 14))
|
||
.multilineTextAlignment(.center)
|
||
.lineLimit(2)
|
||
}
|
||
.padding(16)
|
||
.background(
|
||
RoundedRectangle(cornerRadius: 12)
|
||
.fill(Color.black.opacity(0.6))
|
||
)
|
||
.frame(maxWidth: 250)
|
||
|
||
Spacer()
|
||
}
|
||
Spacer()
|
||
}
|
||
}
|
||
}
|
||
|
||
// MARK: - Preview
|
||
|
||
#if DEBUG
|
||
struct APILoadingEffectView_Previews: PreviewProvider {
|
||
static var previews: some View {
|
||
ZStack {
|
||
// 模拟背景
|
||
Rectangle()
|
||
.fill(Color.blue.opacity(0.3))
|
||
.ignoresSafeArea()
|
||
|
||
VStack(spacing: 20) {
|
||
Text("背景内容")
|
||
.font(.title)
|
||
|
||
Button("测试按钮") {
|
||
debugInfo("按钮被点击了!")
|
||
}
|
||
.padding()
|
||
.background(Color.blue)
|
||
.foregroundColor(.white)
|
||
.cornerRadius(8)
|
||
}
|
||
|
||
// Loading Effect View
|
||
APILoadingEffectView()
|
||
}
|
||
.previewDisplayName("API Loading Effect")
|
||
.onAppear {
|
||
// 模拟不同状态的预览
|
||
Task {
|
||
let manager = APILoadingManager.shared
|
||
|
||
// 添加 loading
|
||
let id1 = await manager.startLoading()
|
||
|
||
// 2秒后添加错误
|
||
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
||
Task {
|
||
await manager.setError(id1, errorMessage: "网络连接失败,请检查网络设置")
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// MARK: - Preview Helpers
|
||
|
||
/// 预览用的测试状态
|
||
private struct PreviewStateModifier: ViewModifier {
|
||
let showLoading: Bool
|
||
let showError: Bool
|
||
let errorMessage: String
|
||
|
||
func body(content: Content) -> some View {
|
||
content
|
||
.onAppear {
|
||
Task {
|
||
let manager = APILoadingManager.shared
|
||
|
||
if showLoading {
|
||
let _ = await manager.startLoading()
|
||
}
|
||
|
||
if showError {
|
||
let id = await manager.startLoading()
|
||
try? await Task.sleep(nanoseconds: 1_000_000_000) // 1秒
|
||
await manager.setError(id, errorMessage: errorMessage)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
extension View {
|
||
/// 添加预览状态
|
||
func previewLoadingState(
|
||
showLoading: Bool = false,
|
||
showError: Bool = false,
|
||
errorMessage: String = "示例错误信息"
|
||
) -> some View {
|
||
self.modifier(PreviewStateModifier(
|
||
showLoading: showLoading,
|
||
showError: showError,
|
||
errorMessage: errorMessage
|
||
))
|
||
}
|
||
}
|
||
#endif |