107 lines
3.8 KiB
Swift
107 lines
3.8 KiB
Swift
import SwiftUI
|
||
|
||
struct AsyncImageView: View {
|
||
let url: String
|
||
let nums: [Int]
|
||
let index: Int
|
||
@State private var image: UIImage?
|
||
@State private var isLoading = false
|
||
@State private var error: Error?
|
||
|
||
// 静态属性,用于维护当前加载的索引范围
|
||
private static var currentlyLoadingIndices: Set<Int> = []
|
||
private static let maxConcurrentLoads = 4
|
||
|
||
var body: some View {
|
||
Group {
|
||
if let image = image {
|
||
Image(uiImage: image)
|
||
.resizable()
|
||
.aspectRatio(contentMode: .fit)
|
||
} else if isLoading {
|
||
ProgressView()
|
||
} else if error != nil {
|
||
VStack {
|
||
Image(systemName: "exclamationmark.triangle")
|
||
.foregroundColor(.red)
|
||
Text(error?.localizedDescription ?? "未知错误")
|
||
.font(.caption)
|
||
}
|
||
} else {
|
||
Color.gray.opacity(0.3)
|
||
}
|
||
}
|
||
.onAppear {
|
||
loadImage()
|
||
}
|
||
}
|
||
|
||
private func loadImage() {
|
||
// 如果已经在加载中,或者超出最大加载限制,直接返回
|
||
guard !Self.currentlyLoadingIndices.contains(index),
|
||
Self.currentlyLoadingIndices.count < Self.maxConcurrentLoads else {
|
||
return
|
||
}
|
||
|
||
// 添加当前索引到加载集合
|
||
Self.currentlyLoadingIndices.insert(index)
|
||
isLoading = true
|
||
error = nil
|
||
|
||
// 确保 nums 和 index 对齐,避免越界
|
||
guard let decodeNum = nums[safe: index] else {
|
||
print("错误:nums 中未找到索引 \(index) 对应的值,可能 API 数据不一致")
|
||
Self.currentlyLoadingIndices.remove(index)
|
||
return
|
||
}
|
||
|
||
print("调试信息:图片 URL: \(url), 解密参数 num: \(decodeNum), 索引: \(index)")
|
||
|
||
guard let imageUrl = URL(string: url) else {
|
||
self.error = NSError(domain: "无效的URL", code: 1, userInfo: nil)
|
||
isLoading = false
|
||
Self.currentlyLoadingIndices.remove(index)
|
||
return
|
||
}
|
||
|
||
// 加载图片数据
|
||
URLSession.shared.dataTask(with: imageUrl) { data, response, error in
|
||
DispatchQueue.main.async {
|
||
// 移除当前索引
|
||
Self.currentlyLoadingIndices.remove(index)
|
||
self.isLoading = false
|
||
|
||
if let error = error {
|
||
self.error = error
|
||
print("图片加载失败,错误: \(error.localizedDescription)")
|
||
return
|
||
}
|
||
|
||
guard let httpResponse = response as? HTTPURLResponse,
|
||
(200...299).contains(httpResponse.statusCode),
|
||
let data = data, !data.isEmpty else {
|
||
self.error = NSError(domain: "无效响应", code: 2, userInfo: nil)
|
||
print("图片加载失败,无效响应或数据为空")
|
||
return
|
||
}
|
||
|
||
if let loadedImage = UIImage(data: data) {
|
||
print("图片加载成功,开始解密...")
|
||
let decodedImage = loadedImage.decodeImage(num: decodeNum)
|
||
self.image = decodedImage
|
||
print("解密完成,图片已更新")
|
||
} else {
|
||
self.error = NSError(domain: "图片数据解码失败", code: 4, userInfo: nil)
|
||
print("图片数据解码失败")
|
||
}
|
||
}
|
||
}.resume()
|
||
}
|
||
}
|
||
|
||
extension Array {
|
||
subscript(safe index: Int) -> Element? {
|
||
return indices.contains(index) ? self[index] : nil
|
||
}
|
||
}
|