| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- //
- // 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)
- ])
- }
- }
|