// // LoadingViewController.swift // QuickSearchLocation // // Created by Destiny on 2024/12/31. // import UIKit import Photos class LoadingViewController: UIViewController { private let photoClassifier = PhotoClassifier() private let progressView = UIProgressView(progressViewStyle: .default) private let statusLabel = UILabel() private let resultLabel = UILabel() private let collectionView: UICollectionView private var similarGroups: [[PHAsset]] = [] private var timer: Timer? // 添加详细进度标签 private let detailProgressLabel = UILabel() private let percentageLabel = UILabel() override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { // 初始化 CollectionView let layout = UICollectionViewFlowLayout() layout.scrollDirection = .vertical layout.minimumLineSpacing = 10 layout.minimumInteritemSpacing = 5 let screenWidth = UIScreen.main.bounds.width let width = (screenWidth - 20) / 3 layout.itemSize = CGSize(width: width, height: width) layout.sectionInset = UIEdgeInsets(top: 10, left: 5, bottom: 10, right: 5) layout.headerReferenceSize = CGSize(width: screenWidth, height: 40) collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() setupUI() startClassifying() } private func setupUI() { view.backgroundColor = .white // 设置进度容器视图 let progressContainer = UIView() progressContainer.translatesAutoresizingMaskIntoConstraints = false view.addSubview(progressContainer) // 配置所有标签 statusLabel.textAlignment = .center statusLabel.font = .systemFont(ofSize: 16, weight: .medium) detailProgressLabel.textAlignment = .center detailProgressLabel.font = .systemFont(ofSize: 14) detailProgressLabel.textColor = .darkGray percentageLabel.textAlignment = .center percentageLabel.font = .systemFont(ofSize: 14, weight: .medium) percentageLabel.textColor = .systemBlue resultLabel.textAlignment = .center resultLabel.numberOfLines = 0 resultLabel.font = .systemFont(ofSize: 14) // 配置进度条 progressView.progressTintColor = .systemBlue progressView.trackTintColor = .systemGray5 // 添加所有视图 [statusLabel, progressView, detailProgressLabel, percentageLabel, resultLabel].forEach { $0.translatesAutoresizingMaskIntoConstraints = false progressContainer.addSubview($0) } // 布局约束 NSLayoutConstraint.activate([ progressContainer.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), progressContainer.leadingAnchor.constraint(equalTo: view.leadingAnchor), progressContainer.trailingAnchor.constraint(equalTo: view.trailingAnchor), progressContainer.heightAnchor.constraint(equalToConstant: 180), statusLabel.topAnchor.constraint(equalTo: progressContainer.topAnchor, constant: 20), statusLabel.centerXAnchor.constraint(equalTo: progressContainer.centerXAnchor), progressView.topAnchor.constraint(equalTo: statusLabel.bottomAnchor, constant: 15), progressView.centerXAnchor.constraint(equalTo: progressContainer.centerXAnchor), progressView.widthAnchor.constraint(equalTo: progressContainer.widthAnchor, multiplier: 0.8), detailProgressLabel.topAnchor.constraint(equalTo: progressView.bottomAnchor, constant: 10), detailProgressLabel.centerXAnchor.constraint(equalTo: progressContainer.centerXAnchor), percentageLabel.topAnchor.constraint(equalTo: detailProgressLabel.bottomAnchor, constant: 5), percentageLabel.centerXAnchor.constraint(equalTo: progressContainer.centerXAnchor), resultLabel.topAnchor.constraint(equalTo: percentageLabel.bottomAnchor, constant: 15), resultLabel.centerXAnchor.constraint(equalTo: progressContainer.centerXAnchor), resultLabel.widthAnchor.constraint(equalTo: progressContainer.widthAnchor, multiplier: 0.8) ]) // 设置 CollectionView collectionView.backgroundColor = .white collectionView.delegate = self collectionView.dataSource = self collectionView.register(PhotoCell.self, forCellWithReuseIdentifier: "PhotoCell") collectionView.register( HeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "Header" ) collectionView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(collectionView) NSLayoutConstraint.activate([ collectionView.topAnchor.constraint(equalTo: progressContainer.bottomAnchor), collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor), collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor), collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor) ]) } private func startClassifying() { // 重置UI progressView.progress = 0 statusLabel.text = "正在准备..." detailProgressLabel.text = "初始化中" percentageLabel.text = "0%" resultLabel.text = "" // 请求相册权限 PHPhotoLibrary.requestAuthorization { [weak self] status in guard let self = self else { return } DispatchQueue.main.async { if status == .authorized { self.beginPhotoClassification() } else { self.statusLabel.text = "需要相册访问权限" self.detailProgressLabel.text = "请在设置中允许访问相册" } } } } private func beginPhotoClassification() { statusLabel.text = "正在加载照片..." let fetchOptions = PHFetchOptions() let allPhotos = PHAsset.fetchAssets(with: .image, options: fetchOptions) // 更新总数 detailProgressLabel.text = "共发现 \(allPhotos.count) 张照片" photoClassifier.classifyPhotos( assets: allPhotos, progressHandler: { [weak self] (stage, progress) in DispatchQueue.main.async { self?.updateProgress(stage: stage, progress: progress) } }, completion: { [weak self] result in guard let self = self else { return } DispatchQueue.main.async { self.updateProgress(stage: "分类完成", progress: 1.0) // 更新结果显示 var resultText = "分类结果:\n" resultText += "截图:\(result.screenshots.count) 张\n" resultText += "相似照片组:\(result.similarPhotos.count) 组" self.resultLabel.text = resultText // 更新相似照片展示 self.similarGroups = result.similarPhotos self.collectionView.reloadData() } } ) } private func updateProgress(stage: String, progress: Float) { statusLabel.text = stage progressView.progress = progress percentageLabel.text = "\(Int(progress * 100))%" switch stage { case "正在加载照片...": detailProgressLabel.text = "正在读取照片数据" case "正在提取特征...": detailProgressLabel.text = "正在分析照片内容" case "正在比较相似度...": detailProgressLabel.text = "正在查找相似照片" case "分类完成": detailProgressLabel.text = "处理完成" default: break } } } // MARK: - UICollectionView DataSource & Delegate extension LoadingViewController: UICollectionViewDataSource, UICollectionViewDelegate { func numberOfSections(in collectionView: UICollectionView) -> Int { return similarGroups.count } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return similarGroups[section].count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell( withReuseIdentifier: "PhotoCell", for: indexPath ) as! PhotoCell let asset = similarGroups[indexPath.section][indexPath.item] cell.configure(with: asset) return cell } func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { if kind == UICollectionView.elementKindSectionHeader { let header = collectionView.dequeueReusableSupplementaryView( ofKind: kind, withReuseIdentifier: "Header", for: indexPath ) as! HeaderView let groupCount = similarGroups[indexPath.section].count header.titleLabel.text = "相似组 \(indexPath.section + 1) (\(groupCount) 张)" return header } return UICollectionReusableView() } } // MARK: - PhotoCell class PhotoCell: UICollectionViewCell { private let imageView = UIImageView() override init(frame: CGRect) { super.init(frame: frame) setupUI() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupUI() { imageView.contentMode = .scaleAspectFill imageView.clipsToBounds = true imageView.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(imageView) NSLayoutConstraint.activate([ imageView.topAnchor.constraint(equalTo: contentView.topAnchor), imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), imageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), imageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) ]) } func configure(with asset: PHAsset) { let options = PHImageRequestOptions() options.deliveryMode = .fastFormat PHImageManager.default().requestImage( for: asset, targetSize: CGSize(width: 200, height: 200), contentMode: .aspectFill, options: options ) { [weak self] image, _ in self?.imageView.image = image } } } // MARK: - HeaderView class HeaderView: UICollectionReusableView { let titleLabel = UILabel() override init(frame: CGRect) { super.init(frame: frame) setupUI() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupUI() { titleLabel.font = .systemFont(ofSize: 16, weight: .medium) titleLabel.translatesAutoresizingMaskIntoConstraints = false addSubview(titleLabel) NSLayoutConstraint.activate([ titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 15), titleLabel.centerYAnchor.constraint(equalTo: centerYAnchor) ]) } }