NCViewerImageCollectionViewCell.swift 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import UIKit
  2. protocol NCViewerImageCollectionViewCellDelegate: class {
  3. func didStartZooming(_ cell: NCViewerImageCollectionViewCell)
  4. }
  5. class NCViewerImageCollectionViewCell: UICollectionViewCell {
  6. static var reusableIdentifier: String = "NCViewerImageCollectionViewCell"
  7. private var dataTask: URLSessionDataTask?
  8. weak var delegate: NCViewerImageCollectionViewCellDelegate?
  9. @IBOutlet weak var imageWidthConstraint: NSLayoutConstraint!
  10. @IBOutlet weak var imageHeightConstraint: NSLayoutConstraint!
  11. @IBOutlet weak var topConstraint: NSLayoutConstraint!
  12. @IBOutlet weak var bottomConstraint: NSLayoutConstraint!
  13. @IBOutlet weak var scrollView: UIScrollView!
  14. @IBOutlet weak var galleryImageView: UIImageView!
  15. @IBOutlet weak var leadingConstraint: NSLayoutConstraint!
  16. @IBOutlet weak var trailingConstraint: NSLayoutConstraint!
  17. private var observer: NSKeyValueObservation?
  18. func withImageAsset(_ asset: NCViewerImageAsset?) {
  19. guard self.dataTask?.state != URLSessionDataTask.State.running else { return }
  20. guard let asset = asset else { return }
  21. if asset.image != nil {
  22. self.apply(image: self.fitIntoFrame(image: asset.image, type: asset.type))
  23. } else if asset.url != nil {
  24. self.galleryImageView.image = nil
  25. self.dataTask = asset.download(completion: { _ in
  26. self.apply(image: self.fitIntoFrame(image: asset.image, type: asset.type))
  27. })
  28. }
  29. }
  30. func apply(image: UIImage?) {
  31. guard let image = image else { return }
  32. self.galleryImageView.alpha = 0
  33. self.galleryImageView.image = image
  34. UIView.animate(withDuration: 0.1) {
  35. self.galleryImageView.alpha = 1
  36. }
  37. }
  38. override func draw(_ rect: CGRect) {
  39. super.draw(rect)
  40. self.scrollView.maximumZoomScale = 4
  41. self.redrawConstraintIfNeeded()
  42. self.observer = self.observe(\.bounds, options: NSKeyValueObservingOptions.new, changeHandler: { (_, _) in
  43. self.apply(image: self.fitIntoFrame(image: self.galleryImageView.image, type: nil))
  44. self.redrawConstraintIfNeeded()
  45. })
  46. }
  47. func cancelPendingDataTask() {
  48. self.dataTask?.cancel()
  49. }
  50. override func layoutSubviews() {
  51. DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
  52. self.redrawConstraintIfNeeded()
  53. }
  54. super.layoutSubviews()
  55. }
  56. override func prepareForReuse() {
  57. super.prepareForReuse()
  58. self.scrollView.setZoomScale(1, animated: false)
  59. DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
  60. // limitation of cell lifecycle
  61. self.redrawConstraintIfNeeded()
  62. }
  63. }
  64. func setMargins(vertical: CGFloat, horizontal: CGFloat) {
  65. self.topConstraint.constant = vertical
  66. self.bottomConstraint.constant = vertical
  67. self.leadingConstraint.constant = horizontal
  68. self.trailingConstraint.constant = horizontal
  69. }
  70. func redrawConstraintIfNeeded() {
  71. let imageHeight = self.galleryImageView.frame.size.height
  72. let imageWidth = self.galleryImageView.frame.size.width
  73. let spaceLeftVertical = self.scrollView.frame.size.height-imageHeight
  74. let spaceLeftHorizontal = self.scrollView.frame.size.width-imageWidth
  75. let constraintConstantValueVertical = spaceLeftVertical/2 > 0 ? spaceLeftVertical/2 : 0
  76. let constraintConstantValueHorizontal = spaceLeftHorizontal/2 > 0 ? spaceLeftHorizontal/2 : 0
  77. self.setMargins(vertical: constraintConstantValueVertical, horizontal: constraintConstantValueHorizontal)
  78. self.layoutIfNeeded()
  79. }
  80. private func fitIntoFrame(image: UIImage?, type: NCViewerImageAsset.ImageType?) -> UIImage? {
  81. let type: NCViewerImageAsset.ImageType = type ?? .jpg
  82. guard let image = image else { return nil }
  83. guard image.size != CGSize.zero else { return nil }
  84. let screenRatio = UIScreen.main.bounds.size.width/UIScreen.main.bounds.size.height
  85. var reqWidth: CGFloat = frame.size.width
  86. if image.size.width > reqWidth {
  87. reqWidth = image.size.width
  88. }
  89. let imageRatio = image.size.width/image.size.height
  90. if imageRatio < screenRatio {
  91. reqWidth = frame.size.height*imageRatio
  92. }
  93. let size = CGSize(width: reqWidth, height: reqWidth/imageRatio)
  94. if imageRatio < screenRatio {
  95. self.imageHeightConstraint.constant = frame.size.height
  96. self.imageWidthConstraint.constant = frame.size.height*imageRatio
  97. } else {
  98. self.imageHeightConstraint.constant = frame.size.width/imageRatio
  99. self.imageWidthConstraint.constant = frame.size.width
  100. }
  101. switch type {
  102. case .gif: return image
  103. case .jpg:
  104. UIGraphicsBeginImageContext(size)
  105. image.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
  106. let finalImage: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
  107. UIGraphicsEndImageContext()
  108. return finalImage
  109. }
  110. }
  111. func redrawImage() {
  112. self.apply(image: self.fitIntoFrame(image: self.galleryImageView.image, type: nil))
  113. self.redrawConstraintIfNeeded()
  114. }
  115. }
  116. extension NCViewerImageCollectionViewCell: UIScrollViewDelegate {
  117. func viewForZooming(in scrollView: UIScrollView) -> UIView? {
  118. return self.galleryImageView
  119. }
  120. func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) {
  121. self.delegate?.didStartZooming(self)
  122. }
  123. func scrollViewDidZoom(_ scrollView: UIScrollView) {
  124. self.redrawConstraintIfNeeded()
  125. }
  126. }