import SwiftUI enum ImagePreviewSource: Identifiable, Equatable { case local(UIImage) case remote(String) var id: String { switch self { case .local(let img): return String(describing: img.hashValue) case .remote(let url): return url } } static func == (lhs: ImagePreviewSource, rhs: ImagePreviewSource) -> Bool { switch (lhs, rhs) { case let (.local(l), .local(r)): return l.pngData() == r.pngData() case let (.remote(l), .remote(r)): return l == r default: return false } } } struct ImagePreviewPager: View { let images: [ImagePreviewSource] @Binding var currentIndex: Int let onClose: () -> Void // 本地图片构造器 init(images: [UIImage], currentIndex: Binding, onClose: @escaping () -> Void) { self.images = images.map { .local($0) } self._currentIndex = currentIndex self.onClose = onClose } // 远程图片构造器 init(images: [String], currentIndex: Binding, onClose: @escaping () -> Void) { self.images = images.map { .remote($0) } self._currentIndex = currentIndex self.onClose = onClose } var body: some View { ZStack(alignment: .topTrailing) { Color.black.ignoresSafeArea() TabView(selection: $currentIndex) { ForEach(Array(images.enumerated()), id: \ .element.id) { idx, source in Group { switch source { case .local(let img): Image(uiImage: img) .resizable() .aspectRatio(contentMode: .fit) .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color.black) case .remote(let urlStr): CachedAsyncImage(url: urlStr) { image in image .resizable() .aspectRatio(contentMode: .fit) .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color.black) } placeholder: { Rectangle() .fill(Color.gray.opacity(0.3)) .overlay( ProgressView() .progressViewStyle(CircularProgressViewStyle(tint: .white.opacity(0.6))) .scaleEffect(0.8) ) } } } .tag(idx) } } .tabViewStyle(PageTabViewStyle(indexDisplayMode: .always)) Button(action: { onClose() }) { Image(systemName: "xmark.circle.fill") .font(.system(size: 32)) .foregroundColor(.white) .padding(24) } } } }