GalleryViewController.swift 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. import UIKit
  2. import DcCore
  3. class GalleryViewController: UIViewController {
  4. // MARK: - data
  5. private let mediaMessageIds: [Int]
  6. // MARK: - subview specs
  7. private let gridDefaultSpacing: CGFloat = 5
  8. private lazy var gridLayout: GridCollectionViewFlowLayout = {
  9. let layout = GridCollectionViewFlowLayout()
  10. layout.minimumLineSpacing = gridDefaultSpacing
  11. layout.minimumInteritemSpacing = gridDefaultSpacing
  12. layout.format = .square
  13. return layout
  14. }()
  15. private lazy var grid: UICollectionView = {
  16. let collection = UICollectionView(frame: .zero, collectionViewLayout: gridLayout)
  17. collection.dataSource = self
  18. collection.delegate = self
  19. collection.register(GalleryCell.self, forCellWithReuseIdentifier: GalleryCell.reuseIdentifier)
  20. collection.contentInset = UIEdgeInsets(top: gridDefaultSpacing, left: gridDefaultSpacing, bottom: gridDefaultSpacing, right: gridDefaultSpacing)
  21. collection.backgroundColor = .white
  22. collection.delaysContentTouches = false
  23. return collection
  24. }()
  25. private lazy var floatingTimeLabel: GalleryTimeLabel = {
  26. let view = GalleryTimeLabel()
  27. view.hide(animated: false)
  28. return view
  29. }()
  30. init(mediaMessageIds: [Int]) {
  31. self.mediaMessageIds = mediaMessageIds.reversed()
  32. super.init(nibName: nil, bundle: nil)
  33. }
  34. required init?(coder: NSCoder) {
  35. fatalError("init(coder:) has not been implemented")
  36. }
  37. // MARK: - lifecycle
  38. override func viewDidLoad() {
  39. super.viewDidLoad()
  40. setupSubviews()
  41. title = String.localized("gallery")
  42. }
  43. override func viewWillAppear(_ animated: Bool) {
  44. grid.reloadData()
  45. }
  46. override func viewWillLayoutSubviews() {
  47. super.viewWillLayoutSubviews()
  48. self.reloadCollectionViewLayout()
  49. }
  50. // MARK: - setup
  51. private func setupSubviews() {
  52. view.addSubview(grid)
  53. grid.translatesAutoresizingMaskIntoConstraints = false
  54. grid.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
  55. grid.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
  56. grid.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true
  57. grid.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
  58. view.addSubview(floatingTimeLabel)
  59. floatingTimeLabel.translatesAutoresizingMaskIntoConstraints = false
  60. floatingTimeLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10).isActive = true
  61. floatingTimeLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
  62. }
  63. // MARK: - updates
  64. private func updateFloatingTimeLabel() {
  65. if let indexPath = grid.indexPathsForVisibleItems.min() {
  66. let msgId = mediaMessageIds[indexPath.row]
  67. let msg = DcMsg(id: msgId)
  68. floatingTimeLabel.update(date: msg.sentDate)
  69. }
  70. }
  71. }
  72. // MARK: - UICollectionViewDataSource, UICollectionViewDelegate
  73. extension GalleryViewController: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
  74. func numberOfSections(in collectionView: UICollectionView) -> Int {
  75. return 1
  76. }
  77. func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
  78. return mediaMessageIds.count
  79. }
  80. func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
  81. guard let mediaCell = collectionView.dequeueReusableCell(
  82. withReuseIdentifier: GalleryCell.reuseIdentifier,
  83. for: indexPath) as? GalleryCell else {
  84. return UICollectionViewCell()
  85. }
  86. let msgId = mediaMessageIds[indexPath.row]
  87. let msg = DcMsg(id: msgId)
  88. mediaCell.update(msg: msg)
  89. return mediaCell
  90. }
  91. func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
  92. let msgId = mediaMessageIds[indexPath.row]
  93. showPreview(msgId: msgId)
  94. }
  95. func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
  96. updateFloatingTimeLabel()
  97. floatingTimeLabel.show(animated: true)
  98. }
  99. func scrollViewDidScroll(_ scrollView: UIScrollView) {
  100. updateFloatingTimeLabel()
  101. }
  102. func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
  103. floatingTimeLabel.hide(animated: true)
  104. }
  105. }
  106. // MARK: - update layout
  107. extension GalleryViewController {
  108. private func reloadCollectionViewLayout() {
  109. // columns specification
  110. let phonePortrait = 2
  111. let phoneLandscape = 3
  112. let padPortrait = 4
  113. let padLandscape = 6
  114. let orientation = UIApplication.shared.statusBarOrientation
  115. let deviceType = UIDevice.current.userInterfaceIdiom
  116. var gridDisplay: GridDisplay?
  117. if deviceType == .phone {
  118. if orientation.isPortrait {
  119. gridDisplay = .grid(columns: phonePortrait)
  120. } else {
  121. gridDisplay = .grid(columns: phoneLandscape)
  122. }
  123. } else if deviceType == .pad {
  124. if orientation.isPortrait {
  125. gridDisplay = .grid(columns: padPortrait)
  126. } else {
  127. gridDisplay = .grid(columns: padLandscape)
  128. }
  129. }
  130. if let gridDisplay = gridDisplay {
  131. gridLayout.display = gridDisplay
  132. } else {
  133. safe_fatalError("undefined format")
  134. }
  135. let containerWidth = view.bounds.width - view.safeAreaInsets.left - view.safeAreaInsets.right - 2 * gridDefaultSpacing
  136. gridLayout.containerWidth = containerWidth
  137. }
  138. }
  139. // MARK: - coordinator
  140. extension GalleryViewController {
  141. func showPreview(msgId: Int) {
  142. let msg = DcMsg(id: msgId)
  143. guard let url = msg.fileURL, let index = mediaMessageIds.index(of: msgId) else {
  144. return
  145. }
  146. let previousUrls: [URL] = msg.previousMediaURLs()
  147. let nextUrls: [URL] = msg.nextMediaURLs()
  148. // these are the files user will be able to swipe trough
  149. let mediaUrls: [URL] = previousUrls + [url] + nextUrls
  150. let previewController = PreviewController(currentIndex: index, urls: mediaUrls)
  151. present(previewController, animated: true, completion: nil)
  152. }
  153. }