diff --git a/dimensionhub/Core/CacheManager.swift b/dimensionhub/Core/CacheManager.swift index ab86d41..a78eaf7 100644 --- a/dimensionhub/Core/CacheManager.swift +++ b/dimensionhub/Core/CacheManager.swift @@ -37,6 +37,18 @@ final class CacheManager { } } + // 预加载图片 + func preloadImage(url: String) async { + guard !self.fileExists(urlString: url) else { + return + } + + if let filename = self.getCacheFileName(urlString: url), + let data = try? await self.downloadImage(from: url) { + try? self.saveCacheFile(filename: filename, data: data) + } + } + // 判断缓存文件是否存在 func fileExists(urlString: String) -> Bool { guard let cacheFileName = getCacheFileName(urlString: urlString) else { diff --git a/dimensionhub/Views/FlexImage.swift b/dimensionhub/Views/FlexImage.swift new file mode 100644 index 0000000..ba9401b --- /dev/null +++ b/dimensionhub/Views/FlexImage.swift @@ -0,0 +1,73 @@ +// +// FlexImage.swift +// dimensionhub +// +// Created by 安礼成 on 2025/4/14. +// + +import SwiftUI + +struct FlexImage: View { + let urlString: String + let width: CGFloat + let height: CGFloat + let placeholder: String + let mode: ImageMode + + // 图片加载模式 + enum ImageMode { + case remote(String) + case local(Data) + } + + init(urlString: String, width: CGFloat, height: CGFloat, placeholder: String) { + self.urlString = urlString + self.width = width + self.height = height + self.placeholder = placeholder + + let cacheManager = CacheManager.shared + if let data = cacheManager.readFileContents(urlString: urlString) { + self.mode = .local(data) + } else { + Task.detached { + await cacheManager.preloadImage(url: urlString) + } + self.mode = .remote(urlString) + } + } + + var body: some View { + switch self.mode { + case .remote(let url): + AsyncImage(url: URL(string: url)) { phase in + switch phase { + case .empty: + ProgressView() + case .success(let image): + image + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: width, height: height) + .clipped() + default: + Image(placeholder) + .resizable() + .aspectRatio(contentMode: .fill) + .clipped() + } + } + case .local(let data): + Image(uiImage: UIImage(data: data)!) + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: width, height: height) + .clipped() + } + } + +} + +#Preview { + //FlexImage() +} diff --git a/dimensionhub/Views/Index/IndexMainView.swift b/dimensionhub/Views/Index/IndexMainView.swift index e518989..789c80f 100644 --- a/dimensionhub/Views/Index/IndexMainView.swift +++ b/dimensionhub/Views/Index/IndexMainView.swift @@ -189,52 +189,35 @@ extension IndexMainView { Button(action: { appNav.append(dest: .detail(id: item.id)) }) { - AsyncImage(url: URL(string: item.thumb)) { phase in - switch phase { - case .empty: - ProgressView() - case .success(let image): - image - .resizable() - .aspectRatio(contentMode: .fill) - .frame(width: 370, height: 180) - .clipped() - default: - Image("ph_img_big") - .resizable() - .aspectRatio(contentMode: .fill) - .clipped() + FlexImage(urlString: item.thumb, width: 370, height: 180, placeholder: "ph_img_big") + .frame(width: 370, height: 180) + .overlay(alignment: .topLeading) { + VStack(alignment: .leading, spacing: 8) { + Text(item.name) + .font(.system(size: 16)) + .foregroundColor(.white) + .lineLimit(1) + + Text(item.status) + .font(.system(size: 12)) + .foregroundColor(.white) + .lineLimit(1) + } + .padding(5) + .background( + Color.black.opacity(0.6) + ) + .cornerRadius(5) + .padding(8) } - - } - .frame(width: 370, height: 180) - .overlay(alignment: .topLeading) { - VStack(alignment: .leading, spacing: 8) { - Text(item.name) - .font(.system(size: 16)) - .foregroundColor(.white) - .lineLimit(1) + .background(GeometryReader { geometry in + let height = geometry.size.height + let minY = geometry.frame(in: .named("indexScrollView")).minY + let y = minY >= 0 ? minY : minY + height - Text(item.status) - .font(.system(size: 12)) - .foregroundColor(.white) - .lineLimit(1) - } - .padding(5) - .background( - Color.black.opacity(0.6) - ) - .cornerRadius(5) - .padding(8) - } - .background(GeometryReader { geometry in - let height = geometry.size.height - let minY = geometry.frame(in: .named("indexScrollView")).minY - let y = minY >= 0 ? minY : minY + height - - Color.clear - .preference(key: DramaGroupElementPreferenceKey.self, value: [ group.group_id : y]) - }) + Color.clear + .preference(key: DramaGroupElementPreferenceKey.self, value: [ group.group_id : y]) + }) } } }