从 iOS 17 Observation 迁移至 iOS 16 兼容的 Combine 架构
将最低部署版本降至 iOS 16,以 ObservableObject 替换 @Observable,新增导航与 UI 兼容层,并补充登录冒烟 UI 测试。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
75
suixinkan/Core/Design/AppContentUnavailableView.swift
Normal file
75
suixinkan/Core/Design/AppContentUnavailableView.swift
Normal file
@ -0,0 +1,75 @@
|
||||
//
|
||||
// AppContentUnavailableView.swift
|
||||
// suixinkan
|
||||
//
|
||||
// Created by Codex on 2026/6/26.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
/// iOS 16 兼容的空状态视图;iOS 17+ 自动使用系统 AppContentUnavailableView。
|
||||
struct AppContentUnavailableView<LabelContent: View, DescriptionContent: View, ActionsContent: View>: View {
|
||||
private let label: LabelContent
|
||||
private let description: DescriptionContent
|
||||
private let actions: ActionsContent
|
||||
|
||||
init(
|
||||
@ViewBuilder label: () -> LabelContent,
|
||||
@ViewBuilder description: () -> DescriptionContent,
|
||||
@ViewBuilder actions: () -> ActionsContent
|
||||
) {
|
||||
self.label = label()
|
||||
self.description = description()
|
||||
self.actions = actions()
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
if #available(iOS 17.0, *) {
|
||||
AppContentUnavailableView {
|
||||
label
|
||||
} description: {
|
||||
description
|
||||
} actions: {
|
||||
actions
|
||||
}
|
||||
} else {
|
||||
VStack(spacing: AppMetrics.Spacing.small) {
|
||||
label
|
||||
.font(.system(size: AppMetrics.FontSize.title3, weight: .semibold))
|
||||
.foregroundStyle(AppDesign.textPrimary)
|
||||
description
|
||||
.font(.system(size: AppMetrics.FontSize.subheadline))
|
||||
.foregroundStyle(AppDesign.textSecondary)
|
||||
.multilineTextAlignment(.center)
|
||||
actions
|
||||
.padding(.top, AppMetrics.Spacing.xSmall)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(AppMetrics.Spacing.large)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AppContentUnavailableView where LabelContent == Label<Text, Image>, DescriptionContent == EmptyView, ActionsContent == EmptyView {
|
||||
init(_ title: String, systemImage: String) {
|
||||
self.init {
|
||||
Label(title, systemImage: systemImage)
|
||||
} description: {
|
||||
EmptyView()
|
||||
} actions: {
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AppContentUnavailableView where LabelContent == Label<Text, Image>, DescriptionContent == Text, ActionsContent == EmptyView {
|
||||
init(_ title: String, systemImage: String, description: Text) {
|
||||
self.init {
|
||||
Label(title, systemImage: systemImage)
|
||||
} description: {
|
||||
description
|
||||
} actions: {
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -77,7 +77,7 @@ final class GlobalLoadingCenter {
|
||||
fileprivate final class GlobalLoadingState: ObservableObject {
|
||||
@Published fileprivate var isVisible = false
|
||||
@Published fileprivate var message = ""
|
||||
fileprivate var activeCount = 0
|
||||
@Published fileprivate var activeCount = 0
|
||||
}
|
||||
|
||||
/// Lottie Loading 动画容器,负责播放主包中的 loading.json。
|
||||
|
||||
Reference in New Issue
Block a user