LNImagePreviewController.swift 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. //
  2. // LNImagePreviewController.swift
  3. // Lanu
  4. //
  5. // Created by OneeChan on 2025/12/11.
  6. //
  7. import Foundation
  8. import UIKit
  9. import SnapKit
  10. extension UIView {
  11. func presentImagePreview(_ urls: [String], _ targetIndex: Int) {
  12. let vc = LNImagePreviewController()
  13. vc.loadImages(urls: urls, targetIndex: targetIndex)
  14. vc.modalPresentationStyle = .overFullScreen
  15. viewController?.present(vc, animated: true)
  16. }
  17. }
  18. class LNImagePreviewController: LNViewController {
  19. private var imageURLs: [String] = []
  20. private var curIndex: Int = 0 {
  21. didSet {
  22. titleLabel.text = "\(curIndex + 1)/\(imageURLs.count)"
  23. }
  24. }
  25. private var collectionView: UICollectionView?
  26. private let fakeBar = LNFakeNaviBar()
  27. private let titleLabel = UILabel()
  28. func loadImages(urls: [String], targetIndex: Int) {
  29. imageURLs = urls
  30. curIndex = targetIndex
  31. collectionView?.reloadData()
  32. DispatchQueue.main.async { [weak self] in
  33. guard let self else { return }
  34. collectionView?.scrollToItem(
  35. at: .init(row: curIndex, section: 0),
  36. at: .centeredHorizontally,
  37. animated: false)
  38. }
  39. }
  40. override func viewDidLoad() {
  41. super.viewDidLoad()
  42. showNavigationBar = false
  43. setupViews()
  44. }
  45. }
  46. extension LNImagePreviewController: LNImagePreviewCellDelegate {
  47. func onImagePreviewCellDragToDismiss(cell: LNImagePreviewCell) {
  48. dismiss(animated: false)
  49. }
  50. }
  51. extension LNImagePreviewController: UICollectionViewDataSource, UICollectionViewDelegate {
  52. func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
  53. imageURLs.count
  54. }
  55. func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
  56. let cell = collectionView.dequeueReusableCell(withReuseIdentifier: LNImagePreviewCell.className, for: indexPath) as! LNImagePreviewCell
  57. cell.update(url: imageURLs[indexPath.row])
  58. cell.delegate = self
  59. return cell
  60. }
  61. func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
  62. let previewCell = cell as! LNImagePreviewCell
  63. previewCell.resetZoom()
  64. }
  65. func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
  66. curIndex = Int(scrollView.contentOffset.x / scrollView.bounds.width)
  67. }
  68. func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
  69. curIndex = Int(scrollView.contentOffset.x / scrollView.bounds.width)
  70. }
  71. }
  72. extension LNImagePreviewController {
  73. private func setupViews() {
  74. view.backgroundColor = .clear
  75. let layout = UICollectionViewFlowLayout()
  76. layout.scrollDirection = .horizontal
  77. layout.itemSize = view.bounds.size
  78. layout.minimumLineSpacing = 0
  79. layout.minimumInteritemSpacing = 0
  80. let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
  81. collectionView.isPagingEnabled = true
  82. collectionView.showsHorizontalScrollIndicator = false
  83. collectionView.backgroundColor = .black
  84. collectionView.register(LNImagePreviewCell.self, forCellWithReuseIdentifier: LNImagePreviewCell.className)
  85. collectionView.dataSource = self
  86. collectionView.delegate = self
  87. view.addSubview(collectionView)
  88. collectionView.snp.makeConstraints { make in
  89. make.edges.equalToSuperview()
  90. }
  91. self.collectionView = collectionView
  92. let fakeBar = buildFakeNavBar()
  93. view.addSubview(fakeBar)
  94. fakeBar.snp.makeConstraints { make in
  95. make.horizontalEdges.equalToSuperview()
  96. make.top.equalToSuperview()
  97. }
  98. }
  99. private func buildFakeNavBar() -> UIView {
  100. fakeBar.showBackButton { [weak self] in
  101. guard let self else { return }
  102. dismiss(animated: true)
  103. }
  104. fakeBar.backButton?.setImage(.init(systemName: "chevron.backward")?.withRenderingMode(.alwaysTemplate), for: .normal)
  105. fakeBar.backButton?.tintColor = .fill
  106. titleLabel.font = .body_l
  107. titleLabel.textColor = .text_1
  108. fakeBar.actionView.addSubview(titleLabel)
  109. titleLabel.snp.makeConstraints { make in
  110. make.center.equalToSuperview()
  111. }
  112. return fakeBar
  113. }
  114. }