NCViewerPhotoView.swift 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. import UIKit
  2. class NCViewerPhotoView: UIScrollView, UIScrollViewDelegate {
  3. let pagePadding: CGFloat = 10
  4. var recycledPages: Set<NCViewerPhotoImageScrollView> = []
  5. var visiblePages: Set<NCViewerPhotoImageScrollView> = []
  6. var firstVisiblePageIndexBeforeRotation: Int!
  7. var singleTap: UITapGestureRecognizer!
  8. var inTilingProcess: Set<String> = []
  9. var currentImageName: String = ""
  10. var metadata = tableMetadata()
  11. var metadatas = [tableMetadata]()
  12. required init?(coder: NSCoder) {
  13. super.init(coder: coder)
  14. }
  15. override init(frame: CGRect) {
  16. super.init(frame: frame)
  17. NotificationCenter.default.addObserver(self, selector: #selector(self.changeTheming), name: NSNotification.Name(rawValue: "changeTheming"), object: nil)
  18. }
  19. @objc func setup(metadata: tableMetadata, view: UIView) {
  20. self.metadata = metadata
  21. if let metadatas = NCManageDatabase.sharedInstance.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND typeFile == %@", metadata.account, metadata.serverUrl, k_metadataTypeFile_image), sorted: "fileName", ascending: true) {
  22. self.metadatas = metadatas
  23. }
  24. // single tap to show or hide navigation bar
  25. singleTap = UITapGestureRecognizer(target: self, action: #selector(handleSingleTap))
  26. addGestureRecognizer(self.singleTap)
  27. showsVerticalScrollIndicator = false
  28. showsHorizontalScrollIndicator = false
  29. isPagingEnabled = true
  30. contentSize = self.contentSizeForPagingScrollView()
  31. delegate = self
  32. view.addSubview(self)
  33. tilePages()
  34. }
  35. @objc func changeTheming() {
  36. backgroundColor = NCBrandColor.sharedInstance.backgroundView
  37. }
  38. //MARK: - Tiling and page configuration
  39. func tilePages() {
  40. // Calculate which pages should now be visible
  41. let visibleBounds = self.bounds
  42. var firstNeededPageIndex: Int = Int(floor(visibleBounds.minX/visibleBounds.width))
  43. var lastNeededPageIndex: Int = Int(floor((visibleBounds.maxX - 1)/visibleBounds.width))
  44. firstNeededPageIndex = max(firstNeededPageIndex, 0)
  45. lastNeededPageIndex = min(lastNeededPageIndex, metadatas.count - 1)
  46. //Recycle no longer needs pages
  47. for page in self.visiblePages {
  48. if page.index < firstNeededPageIndex || page.index > lastNeededPageIndex {
  49. self.recycledPages.insert(page)
  50. page.removeFromSuperview()
  51. }
  52. }
  53. self.visiblePages.subtract(self.recycledPages)
  54. //add missing pages
  55. for index in firstNeededPageIndex...lastNeededPageIndex {
  56. if !self.isDisplayingPage(forIndex: index) {
  57. let page = self.dequeueRecycledPage() ?? NCViewerPhotoImageScrollView()
  58. self.configure(page, for: index)
  59. self.addSubview(page)
  60. self.visiblePages.insert(page)
  61. }
  62. }
  63. }
  64. func dequeueRecycledPage() -> NCViewerPhotoImageScrollView? {
  65. if let page = self.recycledPages.first {
  66. self.recycledPages.removeFirst()
  67. return page
  68. }
  69. return nil
  70. }
  71. func isDisplayingPage(forIndex index: Int) -> Bool {
  72. for page in self.visiblePages {
  73. if page.index == index {
  74. return true
  75. }
  76. }
  77. return false
  78. }
  79. func configure(_ page: NCViewerPhotoImageScrollView, for index: Int) {
  80. self.singleTap.require(toFail: page.zoomingTap)
  81. page.backgroundColor = self.backgroundColor
  82. page.index = index
  83. page.frame = self.frameForPage(at: index)
  84. self.displayImage(at: index, in: page)
  85. }
  86. func displayImage(at index: Int, in page: NCViewerPhotoImageScrollView) {
  87. let metadata = metadatas[index]
  88. if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) == false {
  89. return
  90. }
  91. let imagePath = CCUtility.getDirectoryProviderStorageOcId(metadatas[index].ocId, fileNameView: metadatas[index].fileNameView)!
  92. let tileManager = NCViewerPhotoTileManager()
  93. if !tileManager.needsTilingImage(in: URL(fileURLWithPath: imagePath)) {
  94. let image: UIImage! = UIImage(contentsOfFile: imagePath)
  95. page.display(image)
  96. return
  97. }
  98. /*
  99. if tileManager.tilesMadeForImage(named: imageFileName) {
  100. let size = tileManager.sizeOfTiledImage(named: imageFileName)!
  101. let url = tileManager.urlOfTiledImage(named: imageFileName)
  102. page.displayTiledImage(in: url, size: size)
  103. }
  104. else {
  105. if self.inTilingProcess.contains(imageFileName) {
  106. if let placeholderURL = tileManager.urlOfPlaceholderOfImage(named: imageFileName) {
  107. let image: UIImage! = UIImage(contentsOfFile: placeholderURL.path)
  108. page.display(image)
  109. }
  110. return
  111. }
  112. else {
  113. self.inTilingProcess.insert(imageFileName)
  114. }
  115. tileManager.makeTiledImage(for: imageURL, placeholderCompletion: { (url, error) in
  116. if error == nil {
  117. let image: UIImage! = UIImage(contentsOfFile: url!.path)
  118. page.display(image)
  119. }
  120. }, tilingCompletion: { (imageName, imageSize, error) in
  121. if error == nil, imageName == self.currentImageName {
  122. let url = tileManager.urlOfTiledImage(named: imageName!)
  123. page.displayTiledImage(in: url, size: imageSize!)
  124. if self.inTilingProcess.contains(imageName!) {
  125. self.inTilingProcess.remove(imageName!)
  126. }
  127. }
  128. else {
  129. if error != nil {
  130. print(error!)
  131. }
  132. }
  133. })
  134. }
  135. */
  136. }
  137. //MARK: - ScrollView delegate methods
  138. func scrollViewDidScroll(_ scrollView: UIScrollView) {
  139. self.tilePages()
  140. }
  141. //MARK: - Frame calculations
  142. func frameForPagingScrollView(in size: CGSize? = nil) -> CGRect {
  143. var frame = self.bounds
  144. if size != nil {
  145. frame.size = size!
  146. }
  147. frame.origin.x -= pagePadding
  148. frame.size.width += 2*pagePadding
  149. return frame
  150. }
  151. func contentSizeForPagingScrollView() -> CGSize {
  152. let bounds = self.bounds
  153. return CGSize(width: bounds.size.width*CGFloat(metadatas.count), height: bounds.size.height)
  154. }
  155. func frameForPage(at index: Int) -> CGRect {
  156. let bounds = self.bounds
  157. var pageFrame = bounds
  158. pageFrame.size.width -= 2*pagePadding
  159. pageFrame.origin.x = (bounds.size.width*CGFloat(index)) + pagePadding
  160. return pageFrame
  161. }
  162. func saveCurrentStatesForRotation() {
  163. let visibleBounds = self.bounds
  164. firstVisiblePageIndexBeforeRotation = Int(floor(visibleBounds.minX/visibleBounds.width))
  165. }
  166. func restoreStatesForRotation(in size: CGSize) {
  167. // recalculate contentSize based on current orientation
  168. let pagingScrollViewFrame = self.frameForPagingScrollView(in: size)
  169. self.frame = pagingScrollViewFrame
  170. self.contentSize = self.contentSizeForPagingScrollView()
  171. // adjust frames and configuration of each visible page
  172. for page in visiblePages {
  173. let restorePoint = page.pointToCenterAfterRotation()
  174. let restoreScale = page.scaleToRestoreAfterRotation()
  175. page.frame = self.frameForPage(at: page.index)
  176. page.setMaxMinZoomScaleForCurrentBounds()
  177. page.restoreCenterPoint(to: restorePoint, oldScale: restoreScale)
  178. }
  179. // adjust contentOffset to preserve page location based on values collected prior to location
  180. var contentOffset = CGPoint.zero
  181. let pageWidth = self.bounds.size.width
  182. contentOffset.x = (CGFloat(firstVisiblePageIndexBeforeRotation) * pageWidth)
  183. self.contentOffset = contentOffset
  184. }
  185. //MARK: - Handle Tap
  186. /// Single tap action which hides navigationBar by default implementation
  187. @objc func handleSingleTap() {
  188. /*
  189. let duration: TimeInterval = 0.2
  190. if self.navigationController != nil {
  191. if !self.navigationBarIsHidden {
  192. self.navigationBarIsHidden = true
  193. UIView.animate(withDuration: duration, animations: {
  194. self.navigationController!.navigationBar.alpha = 0
  195. self.updateBackgroundColor()
  196. }, completion: { (finished) in
  197. self.navigationController!.navigationBar.isHidden = true
  198. })
  199. }
  200. else {
  201. self.navigationBarIsHidden = false
  202. UIView.animate(withDuration: duration) {
  203. self.navigationController!.navigationBar.alpha = 1
  204. self.navigationController!.navigationBar.isHidden = false
  205. self.updateBackgroundColor()
  206. }
  207. }
  208. }
  209. */
  210. }
  211. /// Update background color. Default is white / black.
  212. /*
  213. func updateBackgroundColor() {
  214. if !self.navigationBarIsHidden {
  215. self.updateBackground(to: .white)
  216. }
  217. else {
  218. self.updateBackground(to: .black)
  219. }
  220. }
  221. */
  222. /*
  223. func updateBackground(to color: UIColor) {
  224. self.view.backgroundColor = color
  225. pagingScrollView?.backgroundColor = color
  226. for page in visiblePages {
  227. page.backgroundColor = color
  228. }
  229. }
  230. */
  231. }