// // PhotoPickerPreviewVC.swift // QuickLocation // import UIKit import Photos final class PhotoPickerPreviewVC: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { override var preferredStatusBarStyle: UIStatusBarStyle { .lightContent } private let assets: [PHAsset] private let store: PhotoPickerStore private var currentIndex: Int private var lastPageSize: CGSize = .zero init(assets: [PHAsset], startIndex: Int, store: PhotoPickerStore) { self.assets = assets self.store = store self.currentIndex = min(max(startIndex, 0), max(assets.count - 1, 0)) super.init(nibName: nil, bundle: nil) modalPresentationStyle = .fullScreen } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private lazy var closeBtn: UIButton = { let btn = UIButton(type: .custom) btn.setImage(UIImage(named: "PhotoPicker/btn_close"), for: .normal) btn.extendEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) btn.addTarget(self, action: #selector(tapClose), for: .touchUpInside) return btn }() private lazy var titleLab: UILabel = { let label = UILabel() label.text = "预览" label.textColor = .white label.font = .systemFont(ofSize: 17, weight: .medium) return label }() private lazy var collectionView: UICollectionView = { let layout = UICollectionViewFlowLayout() layout.scrollDirection = .horizontal layout.minimumLineSpacing = 0 layout.minimumInteritemSpacing = 0 let cv = UICollectionView(frame: .zero, collectionViewLayout: layout) cv.isPagingEnabled = true cv.showsHorizontalScrollIndicator = false cv.backgroundColor = .black cv.dataSource = self cv.delegate = self cv.register(PhotoPickerPreviewCell.self, forCellWithReuseIdentifier: "cell") return cv }() private lazy var pageLab: UILabel = { let label = UILabel() label.textColor = .white label.font = .systemFont(ofSize: 13, weight: .medium) label.textAlignment = .center label.backgroundColor = UIColor.black.withAlphaComponent(0.45) label.cornerRadius = 12 label.clipsToBounds = true return label }() override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .black view.addSubview(collectionView) view.addSubview(closeBtn) view.addSubview(titleLab) view.addSubview(pageLab) collectionView.layoutChain.edges() closeBtn.layoutChain.left(16).width(32).height(32) closeBtn.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 6).isActive = true titleLab.layoutChain.centerX().centerY(closeBtn) pageLab.layoutChain.centerX().width(52).height(24) pageLab.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -16).isActive = true updatePageLabel() } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() let size = collectionView.bounds.size guard size.width > 1 else { return } if size != lastPageSize { lastPageSize = size collectionView.collectionViewLayout.invalidateLayout() } guard assets.indices.contains(currentIndex) else { return } let offset = CGFloat(currentIndex) * size.width if abs(collectionView.contentOffset.x - offset) > 1 { collectionView.setContentOffset(CGPoint(x: offset, y: 0), animated: false) } } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { assets.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! PhotoPickerPreviewCell cell.configure(asset: assets[indexPath.item], store: store) return cell } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { collectionView.bounds.size } func scrollViewDidScroll(_ scrollView: UIScrollView) { let width = scrollView.bounds.width guard width > 0 else { return } let index = Int(round(scrollView.contentOffset.x / width)) let clamped = min(max(index, 0), max(assets.count - 1, 0)) if clamped != currentIndex { currentIndex = clamped updatePageLabel() } } private func updatePageLabel() { pageLab.text = "\(currentIndex + 1)/\(max(assets.count, 1))" } @objc private func tapClose() { dismiss(animated: true) } } private final class PhotoPickerPreviewCell: UICollectionViewCell { private let imageView = UIImageView() private var assetId = "" private var requestId: PHImageRequestID = PHInvalidImageRequestID override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .black imageView.contentMode = .scaleAspectFit contentView.addSubview(imageView) imageView.layoutChain.edges() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func configure(asset: PHAsset, store: PhotoPickerStore) { if requestId != PHInvalidImageRequestID { store.cancelRequest(requestId) } assetId = asset.localIdentifier imageView.image = nil let size = UIScreen.main.bounds.size requestId = store.requestThumbnail(for: asset, size: size) { [weak self] image in guard let self, self.assetId == asset.localIdentifier else { return } self.imageView.image = image } } }