jsdw_ios/QuickLocation/Component/PhotoPicker/PhotoPickerPreviewVC.swift

164 lines
5.9 KiB
Swift

//
// 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
}
}
}