ImagePreviewCollectionViewCell.swift 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2020 Threema GmbH
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU Affero General Public License, version 3,
  11. // as published by the Free Software Foundation.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU Affero General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Affero General Public License
  19. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. import UIKit
  21. class ImagePreviewCollectionViewCell: UICollectionViewCell, UIScrollViewDelegate, UIGestureRecognizerDelegate {
  22. @IBOutlet weak var scrollView: UIScrollView!
  23. @IBOutlet weak var imageView: UIImageView!
  24. @IBOutlet weak var gifContainerView: UIView!
  25. @IBOutlet weak var gifPlayImageView: UIImageView!
  26. @IBOutlet weak var gifContentVIew: UIView!
  27. @IBOutlet weak var loadingText: UILabel!
  28. @IBOutlet weak var activityIndicator: UIActivityIndicatorView!
  29. @IBOutlet weak var loadingView: UIView!
  30. var indexPath : IndexPath?
  31. var isPlaying : Bool = false
  32. var animatedImageView : FLAnimatedImageView?
  33. var imageSize : CGSize?
  34. required init?(coder: NSCoder) {
  35. super.init(coder: coder)
  36. NotificationCenter.default.addObserver(self, selector: #selector(self.pauseVideo), name: NSNotification.Name(rawValue: kMediaPreviewPauseVideo), object: nil)
  37. }
  38. func addAccessibilityLabels() {
  39. self.imageView.accessibilityLabel = String(format: NSLocalizedString("image", comment: ""))
  40. self.loadingView.accessibilityLabel = String(format: NSLocalizedString("loading_image", comment: ""))
  41. }
  42. func viewForZooming(in scrollView: UIScrollView) -> UIView? {
  43. self.imageView
  44. }
  45. @objc func tapTwice(gesture: UIGestureRecognizer) {
  46. if self.scrollView.zoomScale > self.scrollView.minimumZoomScale {
  47. self.scrollView.setZoomScale(self.scrollView.minimumZoomScale, animated: true)
  48. } else {
  49. self.scrollView.setZoomScale(self.scrollView.maximumZoomScale / 2, animated: true)
  50. }
  51. }
  52. override func prepareForReuse() {
  53. super.prepareForReuse()
  54. self.imageView.image = nil
  55. self.animatedImageView?.animatedImage = nil
  56. self.showLoadingScreen()
  57. }
  58. override func layoutSubviews() {
  59. super.layoutSubviews()
  60. self.animatedImageView?.frame = CGRect(x: 0, y: 0, width: self.bounds.width, height: self.bounds.height)
  61. // This checks if the displayed media type is a static image
  62. // It could be made more robust.
  63. guard let size = self.imageSize else {
  64. return
  65. }
  66. self.setupZooming(size)
  67. self.centerImage()
  68. }
  69. func setColors() {
  70. self.gifPlayImageView.tintColor = .white
  71. self.activityIndicator.color = Colors.fontNormal()
  72. self.loadingText.textColor = Colors.fontNormal()
  73. self.loadingView.backgroundColor = Colors.background()
  74. self.gifPlayImageView.image = self.gifPlayImageView.image?.draw(withTintColor: .white)
  75. }
  76. func showLoadingScreen() {
  77. self.setColors()
  78. self.imageView.isHidden = true
  79. UIView.animate(withDuration: 0.1, animations: {
  80. self.gifContainerView.isHidden = true
  81. self.gifPlayImageView.isHidden = true
  82. self.activityIndicator.startAnimating()
  83. self.loadingText.text = String(format: NSLocalizedString("loading_image", comment: ""))
  84. self.activityIndicator.isHidden = false
  85. self.loadingView.isHidden = false
  86. self.loadingText.isHidden = false
  87. })
  88. self.scrollView.gestureRecognizers?.removeAll()
  89. }
  90. func updateImageTo(data : Data) {
  91. //Setup gif image view
  92. let image = FLAnimatedImage(animatedGIFData: data)
  93. let animImageView = FLAnimatedImageView()
  94. animImageView.animatedImage = image
  95. animImageView.contentMode = .scaleAspectFit
  96. animImageView.frame = self.bounds
  97. // Add gif image view and bring the play button to the foreground
  98. self.gifContainerView.addSubview(animImageView)
  99. self.gifContainerView.bringSubviewToFront(self.gifPlayImageView)
  100. // Store a reference to the image view to be able to relayout it when layoutSubviews is called
  101. self.animatedImageView = animImageView
  102. // FLAnimatedImageView starts animating immediately by default
  103. animatedImageView?.stopAnimating()
  104. // Hide loading screen and unhide gif image view
  105. self.gifPlayImageView.isHidden = false
  106. self.gifContainerView.isHidden = false
  107. self.loadingView.isHidden = true
  108. self.loadingText.isHidden = true
  109. self.activityIndicator.stopAnimating()
  110. let imageFrame = AVMakeRect(aspectRatio: image!.size, insideRect: animImageView.frame)
  111. let tapView = UIView(frame: imageFrame)
  112. self.gifContainerView.addSubview(tapView)
  113. self.gifContainerView.bringSubviewToFront(tapView)
  114. // Add a tap gesture recognizer for play pause
  115. let tapGR = UITapGestureRecognizer(target: self, action: #selector(ImagePreviewCollectionViewCell.handleTap(_:)))
  116. tapGR.delegate = self
  117. tapGR.numberOfTapsRequired = 1
  118. self.animatedImageView!.isUserInteractionEnabled = true
  119. tapView.addGestureRecognizer(tapGR)
  120. }
  121. func updateImageTo(image : UIImage) {
  122. self.imageView.image = image
  123. //
  124. // Setup imageview
  125. self.imageView.contentMode = .scaleAspectFit
  126. // Show imageview and hide loading view
  127. self.imageView.isHidden = false
  128. self.loadingView.isHidden = true
  129. self.activityIndicator.stopAnimating()
  130. self.loadingText.isHidden = true
  131. self.imageSize = image.size
  132. self.setupZooming(image.size)
  133. self.centerImage()
  134. }
  135. private func setupZooming(_ imageSize: CGSize) {
  136. let minZoom = min(self.bounds.size.width / imageSize.width, self.bounds.size.height / imageSize.height);
  137. self.imageView.isUserInteractionEnabled = true
  138. self.imageView.isMultipleTouchEnabled = true
  139. let doubleTapGesture = UITapGestureRecognizer(target: self, action: #selector(self.tapTwice))
  140. doubleTapGesture.numberOfTapsRequired = 2;
  141. self.scrollView.gestureRecognizers?.append(doubleTapGesture)
  142. self.scrollView.contentSize = self.imageView.frame.size
  143. self.scrollView.delegate = self
  144. self.scrollView.isScrollEnabled = false
  145. self.scrollView.minimumZoomScale = minZoom
  146. self.scrollView.setZoomScale(minZoom, animated: false)
  147. self.scrollView.maximumZoomScale = 6
  148. // Set the image frame size after scaling down
  149. let imageWidth = imageSize.width * minZoom
  150. let imageHeight = imageSize.height * minZoom
  151. let newImageFrame = CGRect(x: 0, y: 0, width: imageWidth, height: imageHeight)
  152. self.imageView.frame = newImageFrame
  153. }
  154. private func centerImage() {
  155. let imageViewSize = imageView.frame.size
  156. let scrollViewSize = self.frame.size
  157. let verticalPadding = imageViewSize.height < scrollViewSize.height ? (scrollViewSize.height - imageViewSize.height) / 2 : 0
  158. let horizontalPadding = imageViewSize.width < scrollViewSize.width ? (scrollViewSize.width - imageViewSize.width) / 2 : 0
  159. self.scrollView.contentInset = UIEdgeInsets(top: verticalPadding, left: horizontalPadding, bottom: verticalPadding, right: horizontalPadding)
  160. }
  161. @objc func handleTap(_ gesture: UITapGestureRecognizer) {
  162. DispatchQueue.main.async {
  163. self.togglePlaying()
  164. }
  165. }
  166. func togglePlaying() {
  167. if self.isPlaying {
  168. self.pauseVideo()
  169. } else {
  170. self.playVideo()
  171. }
  172. self.isPlaying = !self.isPlaying
  173. }
  174. @objc func pauseVideo() {
  175. self.animatedImageView?.stopAnimating()
  176. self.gifPlayImageView.isHidden = false
  177. }
  178. func playVideo() {
  179. self.animatedImageView?.startAnimating()
  180. self.gifPlayImageView.isHidden = true
  181. }
  182. }