// // LNImagePreviewController.swift // Lanu // // Created by OneeChan on 2025/12/11. // import Foundation import UIKit import SnapKit extension UIView { func presentImagePreview(_ urls: [String], _ targetIndex: Int) { let vc = LNImagePreviewController() vc.loadImages(urls: urls, targetIndex: targetIndex) vc.modalPresentationStyle = .overFullScreen viewController?.present(vc, animated: true) } } class LNImagePreviewController: LNViewController { private var imageURLs: [String] = [] private var curIndex: Int = 0 { didSet { titleLabel.text = "\(curIndex + 1)/\(imageURLs.count)" } } private var collectionView: UICollectionView? private let fakeBar = LNFakeNaviBar() private let titleLabel = UILabel() func loadImages(urls: [String], targetIndex: Int) { imageURLs = urls curIndex = targetIndex collectionView?.reloadData() runOnMain { [weak self] in guard let self else { return } collectionView?.scrollToItem( at: .init(row: curIndex, section: 0), at: .centeredHorizontally, animated: false) } } override func viewDidLoad() { super.viewDidLoad() showNavigationBar = false setupViews() } } extension LNImagePreviewController: LNImagePreviewCellDelegate { func onImagePreviewCellDragToDismiss(cell: LNImagePreviewCell) { dismiss(animated: false) } } extension LNImagePreviewController: UICollectionViewDataSource, UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { imageURLs.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: LNImagePreviewCell.className, for: indexPath) as! LNImagePreviewCell cell.update(url: imageURLs[indexPath.row]) cell.delegate = self return cell } func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { let previewCell = cell as! LNImagePreviewCell previewCell.resetZoom() } func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { curIndex = Int(scrollView.contentOffset.x / scrollView.bounds.width) } func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { curIndex = Int(scrollView.contentOffset.x / scrollView.bounds.width) } } extension LNImagePreviewController { private func setupViews() { view.backgroundColor = .clear let layout = UICollectionViewFlowLayout() layout.scrollDirection = .horizontal layout.itemSize = view.bounds.size layout.minimumLineSpacing = 0 layout.minimumInteritemSpacing = 0 let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) collectionView.isPagingEnabled = true collectionView.showsHorizontalScrollIndicator = false collectionView.backgroundColor = .black collectionView.register(LNImagePreviewCell.self, forCellWithReuseIdentifier: LNImagePreviewCell.className) collectionView.dataSource = self collectionView.delegate = self view.addSubview(collectionView) collectionView.snp.makeConstraints { make in make.edges.equalToSuperview() } self.collectionView = collectionView let fakeBar = buildFakeNavBar() view.addSubview(fakeBar) fakeBar.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview() make.top.equalToSuperview() } } private func buildFakeNavBar() -> UIView { fakeBar.showBackButton { [weak self] in guard let self else { return } dismiss(animated: true) } fakeBar.backButton?.setImage(.init(systemName: "chevron.backward")?.withRenderingMode(.alwaysTemplate), for: .normal) fakeBar.backButton?.tintColor = .fill titleLabel.font = .body_l titleLabel.textColor = .text_1 fakeBar.actionView.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.center.equalToSuperview() } return fakeBar } }