284 lines
9.8 KiB
Swift
284 lines
9.8 KiB
Swift
import SwiftUI
|
|
import os.log
|
|
|
|
/// 多图上传示例视图
|
|
/// 展示如何使用 MultiImageUploader 组件实现多图上传功能
|
|
@available(iOS 16.0, *)
|
|
struct MultiImageUploadExampleView: View {
|
|
// MARK: - 状态属性
|
|
|
|
@State private var uploadResults: [UploadResult] = []
|
|
@State private var isShowingUploader = false
|
|
@State private var showUploadAlert = false
|
|
@State private var alertMessage = ""
|
|
@State private var isUploading = false
|
|
|
|
private let logger = Logger(subsystem: "com.yourapp.uploader", category: "MultiImageUploadExample")
|
|
|
|
// MARK: - 视图主体
|
|
|
|
var body: some View {
|
|
NavigationView {
|
|
VStack(spacing: 16) {
|
|
// 上传按钮和状态
|
|
uploadButton
|
|
|
|
// 上传统计信息
|
|
if !uploadResults.isEmpty {
|
|
uploadStatsView
|
|
}
|
|
|
|
// 上传进度列表
|
|
uploadProgressList
|
|
|
|
Spacer()
|
|
}
|
|
.navigationTitle("多图上传示例")
|
|
.toolbar {
|
|
// 清空按钮
|
|
if !uploadResults.isEmpty && !isUploading {
|
|
Button("清空") {
|
|
withAnimation {
|
|
uploadResults.removeAll()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.sheet(isPresented: $isShowingUploader) {
|
|
// 多图上传组件
|
|
MultiImageUploader(
|
|
maxSelection: 10
|
|
) { results in
|
|
processUploadResults(results)
|
|
}
|
|
}
|
|
.alert("上传结果", isPresented: $showUploadAlert) {
|
|
Button("确定", role: .cancel) { }
|
|
} message: {
|
|
Text(alertMessage)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - 子视图
|
|
|
|
/// 上传按钮
|
|
private var uploadButton: some View {
|
|
Button(action: { isShowingUploader = true }) {
|
|
Label("选择并上传图片", systemImage: "photo.on.rectangle.angled")
|
|
.font(.headline)
|
|
.foregroundColor(.white)
|
|
.padding()
|
|
.frame(maxWidth: .infinity)
|
|
.background(Color.blue)
|
|
.cornerRadius(10)
|
|
}
|
|
.padding(.horizontal)
|
|
.padding(.top)
|
|
}
|
|
|
|
/// 上传统计信息
|
|
private var uploadStatsView: some View {
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
let successCount = uploadResults.filter { $0.status == .success }.count
|
|
let inProgressCount = uploadResults.filter {
|
|
if case .uploading = $0.status { return true }
|
|
return false
|
|
}.count
|
|
let failedCount = uploadResults.count - successCount - inProgressCount
|
|
|
|
Text("上传统计")
|
|
.font(.headline)
|
|
|
|
HStack {
|
|
StatView(
|
|
value: "\(uploadResults.count)",
|
|
label: "总数量",
|
|
color: .blue
|
|
)
|
|
|
|
StatView(
|
|
value: "\(successCount)",
|
|
label: "成功",
|
|
color: .green
|
|
)
|
|
|
|
StatView(
|
|
value: "\(inProgressCount)",
|
|
label: "上传中",
|
|
color: .orange
|
|
)
|
|
|
|
StatView(
|
|
value: "\(failedCount)",
|
|
label: "失败",
|
|
color: .red
|
|
)
|
|
}
|
|
|
|
// 总进度条
|
|
if inProgressCount > 0 {
|
|
let progress = uploadResults.reduce(0.0) { result, uploadResult in
|
|
if case .uploading(let progress) = uploadResult.status {
|
|
return result + progress
|
|
} else if uploadResult.status == .success {
|
|
return result + 1.0
|
|
}
|
|
return result
|
|
} / Double(uploadResults.count)
|
|
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
Text("总进度: \(Int(progress * 100))%")
|
|
.font(.subheadline)
|
|
ProgressView(value: progress)
|
|
.progressViewStyle(LinearProgressViewStyle(tint: .blue))
|
|
}
|
|
.padding(.top, 8)
|
|
}
|
|
}
|
|
.padding()
|
|
.background(Color(.systemGray6))
|
|
.cornerRadius(10)
|
|
.padding(.horizontal)
|
|
}
|
|
|
|
/// 上传进度列表
|
|
private var uploadProgressList: some View {
|
|
List {
|
|
ForEach($uploadResults) { $result in
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
// 图片缩略图和状态
|
|
HStack {
|
|
// 图片缩略图
|
|
Image(uiImage: result.image)
|
|
.resizable()
|
|
.scaledToFill()
|
|
.frame(width: 60, height: 60)
|
|
.clipped()
|
|
.cornerRadius(8)
|
|
.overlay(
|
|
RoundedRectangle(cornerRadius: 8)
|
|
.stroke(borderColor(for: result.status), lineWidth: 1)
|
|
)
|
|
|
|
// 状态和进度
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
Text("图片 \(result.id.uuidString.prefix(8))...")
|
|
.font(.subheadline)
|
|
|
|
switch result.status {
|
|
case .uploading(let progress):
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
Text("上传中: \(Int(progress * 100))%")
|
|
.font(.caption)
|
|
ProgressView(value: progress)
|
|
.progressViewStyle(LinearProgressViewStyle())
|
|
}
|
|
case .success:
|
|
HStack {
|
|
Image(systemName: "checkmark.circle.fill")
|
|
.foregroundColor(.green)
|
|
Text("上传成功")
|
|
.font(.caption)
|
|
.foregroundColor(.green)
|
|
}
|
|
case .failure(let error):
|
|
HStack {
|
|
Image(systemName: "exclamationmark.triangle.fill")
|
|
.foregroundColor(.red)
|
|
Text("上传失败: \(error.localizedDescription)")
|
|
.font(.caption)
|
|
.foregroundColor(.red)
|
|
}
|
|
case .idle:
|
|
Text("等待上传...")
|
|
.font(.caption)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
}
|
|
|
|
Spacer()
|
|
}
|
|
.padding(.vertical, 4)
|
|
}
|
|
.padding(.vertical, 8)
|
|
.listRowSeparator(.hidden)
|
|
}
|
|
}
|
|
.listStyle(PlainListStyle())
|
|
.animation(.easeInOut, value: uploadResults)
|
|
}
|
|
|
|
// MARK: - 辅助方法
|
|
|
|
/// 获取状态对应的边框颜色
|
|
private func borderColor(for status: UploadStatus) -> Color {
|
|
switch status {
|
|
case .success: return .green
|
|
case .failure: return .red
|
|
case .uploading: return .blue
|
|
case .idle: return .gray
|
|
}
|
|
}
|
|
|
|
/// 处理上传结果
|
|
private func processUploadResults(_ results: [UploadResult]) {
|
|
// 更新状态
|
|
isUploading = results.contains { result in
|
|
if case .uploading = result.status { return true }
|
|
return false
|
|
}
|
|
|
|
// 更新结果
|
|
withAnimation {
|
|
uploadResults = results
|
|
isShowingUploader = false
|
|
}
|
|
|
|
// 检查是否全部完成
|
|
let allFinished = !results.contains { result in
|
|
if case .uploading = result.status { return true }
|
|
if case .idle = result.status { return true }
|
|
return false
|
|
}
|
|
|
|
if allFinished {
|
|
let successCount = results.filter { $0.status == .success }.count
|
|
let totalCount = results.count
|
|
alertMessage = "上传完成\n成功: \(successCount)/\(totalCount)"
|
|
showUploadAlert = true
|
|
isUploading = false
|
|
|
|
logger.info("上传完成,成功: \(successCount)/\(totalCount)")
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - 子视图
|
|
|
|
/// 统计信息视图
|
|
private struct StatView: View {
|
|
let value: String
|
|
let label: String
|
|
let color: Color
|
|
|
|
var body: some View {
|
|
VStack {
|
|
Text(value)
|
|
.font(.title3.bold())
|
|
.foregroundColor(color)
|
|
Text(label)
|
|
.font(.caption)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
.frame(maxWidth: .infinity)
|
|
}
|
|
}
|
|
|
|
// MARK: - 预览
|
|
@available(iOS 16.0, *)
|
|
struct MultiImageUploadExampleView_Previews: PreviewProvider {
|
|
static var previews: some View {
|
|
MultiImageUploadExampleView()
|
|
}
|
|
} |