NCViewerPhotoViewController.swift 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. import UIKit
  2. class NCViewerPhotoViewController: UIViewController, UIScrollViewDelegate {
  3. let pagePadding: CGFloat = 10
  4. var pagingScrollView: UIScrollView!
  5. var recycledPages: Set<NCViewerPhotoImageScrollView> = []
  6. var visiblePages: Set<NCViewerPhotoImageScrollView> = []
  7. var firstVisiblePageIndexBeforeRotation: Int!
  8. var singleTap: UITapGestureRecognizer!
  9. var inTilingProcess: Set<String> = []
  10. var currentImageName: String = ""
  11. var metadata = tableMetadata()
  12. var metadatas = [tableMetadata]()
  13. override func viewDidLoad() {
  14. super.viewDidLoad()
  15. // Get Metadatas
  16. 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) {
  17. self.metadatas = metadatas
  18. }
  19. // single tap to show or hide navigation bar
  20. self.singleTap = UITapGestureRecognizer(target: self, action: #selector(handleSingleTap))
  21. self.view.addGestureRecognizer(self.singleTap)
  22. self.pagingScrollView = UIScrollView(frame: self.frameForPagingScrollView())
  23. //self.updateBackgroundColor()
  24. self.pagingScrollView.showsVerticalScrollIndicator = false
  25. self.pagingScrollView.showsHorizontalScrollIndicator = false
  26. self.pagingScrollView.isPagingEnabled = true
  27. self.pagingScrollView.contentSize = self.contentSizeForPagingScrollView()
  28. self.pagingScrollView.delegate = self
  29. if #available(iOS 11.0, *) {
  30. pagingScrollView.contentInsetAdjustmentBehavior = .never
  31. } else {
  32. automaticallyAdjustsScrollViewInsets = false
  33. }
  34. self.view.addSubview(self.pagingScrollView)
  35. self.layoutPagingScrollView()
  36. self.tilePages()
  37. }
  38. //MARK: - Tiling and page configuration
  39. func tilePages() {
  40. // Calculate which pages should now be visible
  41. let visibleBounds = pagingScrollView.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.pagingScrollView.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.view.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.view.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.pagingScrollView.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.pagingScrollView.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. //MARK: - Rotation Configuration
  163. override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
  164. self.saveCurrentStatesForRotation()
  165. }
  166. override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
  167. self.restoreStatesForRotation(in: size)
  168. }
  169. /**
  170. Save current page and zooming states for device rotation.
  171. */
  172. func saveCurrentStatesForRotation() {
  173. let visibleBounds = pagingScrollView.bounds
  174. firstVisiblePageIndexBeforeRotation = Int(floor(visibleBounds.minX/visibleBounds.width))
  175. }
  176. /**
  177. Apply tracked informations for device rotation.
  178. */
  179. func restoreStatesForRotation(in size: CGSize) {
  180. // recalculate contentSize based on current orientation
  181. let pagingScrollViewFrame = self.frameForPagingScrollView(in: size)
  182. pagingScrollView?.frame = pagingScrollViewFrame
  183. pagingScrollView?.contentSize = self.contentSizeForPagingScrollView()
  184. // adjust frames and configuration of each visible page
  185. for page in visiblePages {
  186. let restorePoint = page.pointToCenterAfterRotation()
  187. let restoreScale = page.scaleToRestoreAfterRotation()
  188. page.frame = self.frameForPage(at: page.index)
  189. page.setMaxMinZoomScaleForCurrentBounds()
  190. page.restoreCenterPoint(to: restorePoint, oldScale: restoreScale)
  191. }
  192. // adjust contentOffset to preserve page location based on values collected prior to location
  193. var contentOffset = CGPoint.zero
  194. let pageWidth = pagingScrollView?.bounds.size.width ?? 1
  195. contentOffset.x = (CGFloat(firstVisiblePageIndexBeforeRotation) * pageWidth)
  196. pagingScrollView?.contentOffset = contentOffset
  197. }
  198. //MARK: - Handle Tap
  199. /// Single tap action which hides navigationBar by default implementation
  200. @objc func handleSingleTap() {
  201. /*
  202. let duration: TimeInterval = 0.2
  203. if self.navigationController != nil {
  204. if !self.navigationBarIsHidden {
  205. self.navigationBarIsHidden = true
  206. UIView.animate(withDuration: duration, animations: {
  207. self.navigationController!.navigationBar.alpha = 0
  208. self.updateBackgroundColor()
  209. }, completion: { (finished) in
  210. self.navigationController!.navigationBar.isHidden = true
  211. })
  212. }
  213. else {
  214. self.navigationBarIsHidden = false
  215. UIView.animate(withDuration: duration) {
  216. self.navigationController!.navigationBar.alpha = 1
  217. self.navigationController!.navigationBar.isHidden = false
  218. self.updateBackgroundColor()
  219. }
  220. }
  221. }
  222. */
  223. }
  224. /// Update background color. Default is white / black.
  225. /*
  226. func updateBackgroundColor() {
  227. if !self.navigationBarIsHidden {
  228. self.updateBackground(to: .white)
  229. }
  230. else {
  231. self.updateBackground(to: .black)
  232. }
  233. }
  234. */
  235. /*
  236. func updateBackground(to color: UIColor) {
  237. self.view.backgroundColor = color
  238. pagingScrollView?.backgroundColor = color
  239. for page in visiblePages {
  240. page.backgroundColor = color
  241. }
  242. }
  243. */
  244. func layoutPagingScrollView() {
  245. self.pagingScrollView.translatesAutoresizingMaskIntoConstraints = false
  246. let top = NSLayoutConstraint(item: self.pagingScrollView!, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1.0, constant: 0.0)
  247. let left = NSLayoutConstraint(item: self.pagingScrollView!, attribute: .left, relatedBy: .equal, toItem: self.view, attribute: .left, multiplier: 1.0, constant: -10.0)
  248. let bottom = NSLayoutConstraint(item: self.pagingScrollView!, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1.0, constant: 0.0)
  249. let right = NSLayoutConstraint(item: self.pagingScrollView!, attribute: .right, relatedBy: .equal, toItem: self.view, attribute: .right, multiplier: 1.0, constant: 10.0)
  250. self.view.addConstraints([top, left, bottom, right])
  251. }
  252. }