浏览代码

start insert new image view

marinofaggiana 4 年之前
父节点
当前提交
28b32b2a78

+ 32 - 20
Nextcloud.xcodeproj/project.pbxproj

@@ -38,6 +38,12 @@
 		F7063DED2199E55F003F38DA /* SVGKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F7063DEC2199E55F003F38DA /* SVGKit.framework */; };
 		F7063DEF2199E568003F38DA /* CocoaLumberjack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F7063DEE2199E568003F38DA /* CocoaLumberjack.framework */; };
 		F7063DF12199E56F003F38DA /* CocoaLumberjackSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F7063DF02199E56E003F38DA /* CocoaLumberjackSwift.framework */; };
+		F70753E32542A99100972D44 /* NCZoomTransitionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70753E02542A99100972D44 /* NCZoomTransitionController.swift */; };
+		F70753E42542A99100972D44 /* NCZoomDismissalInteractionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70753E12542A99100972D44 /* NCZoomDismissalInteractionController.swift */; };
+		F70753E52542A99100972D44 /* NCZoomAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70753E22542A99100972D44 /* NCZoomAnimator.swift */; };
+		F70753EB2542A99800972D44 /* NCPhotoPageContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70753EA2542A99800972D44 /* NCPhotoPageContainerViewController.swift */; };
+		F70753F12542A9A200972D44 /* NCPhotoZoomViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70753F02542A9A200972D44 /* NCPhotoZoomViewController.swift */; };
+		F70753F72542A9C000972D44 /* NCViewerImage.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F70753F62542A9C000972D44 /* NCViewerImage.storyboard */; };
 		F707C26521A2DC5200F6181E /* NCStoreReview.swift in Sources */ = {isa = PBXBuildFile; fileRef = F707C26421A2DC5200F6181E /* NCStoreReview.swift */; };
 		F70968A424212C4E00ED60E5 /* NCLivePhoto.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70968A324212C4E00ED60E5 /* NCLivePhoto.swift */; };
 		F70A58BE24D0349500DED00D /* NCCapabilitiesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70A58BD24D0349500DED00D /* NCCapabilitiesViewController.swift */; };
@@ -80,7 +86,6 @@
 		F72E0B9D21AD60BC00898D7B /* WeScan.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F72E0B9C21AD60BC00898D7B /* WeScan.framework */; };
 		F733B65221997CC2001C1FFA /* TLPhotoPicker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F733B65121997CC1001C1FFA /* TLPhotoPicker.framework */; };
 		F7362A1F220C853A005101B5 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7362A1E220C853A005101B5 /* LaunchScreen.storyboard */; };
-		F738143A240FE48D00404AC7 /* NCViewerImageCommon.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7381439240FE48D00404AC7 /* NCViewerImageCommon.swift */; };
 		F7381EE1218218C9000B1560 /* NCOffline.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7381EDA218218C9000B1560 /* NCOffline.swift */; };
 		F7381EE5218218C9000B1560 /* NCOffline.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7381EDE218218C9000B1560 /* NCOffline.storyboard */; };
 		F738E8421F90FFD100F95C8E /* NCManageEndToEndEncryption.m in Sources */ = {isa = PBXBuildFile; fileRef = F738E8411F90FFD100F95C8E /* NCManageEndToEndEncryption.m */; };
@@ -236,10 +241,6 @@
 		F78F74342163757000C2ADAD /* NCTrash.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F78F74332163757000C2ADAD /* NCTrash.storyboard */; };
 		F78F74362163781100C2ADAD /* NCTrash.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78F74352163781100C2ADAD /* NCTrash.swift */; };
 		F790110E21415BF600D7B136 /* NCViewerRichdocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = F790110D21415BF600D7B136 /* NCViewerRichdocument.swift */; };
-		F79018B6240962C7007C9B6D /* NCViewerImageDismissAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79018B2240962C7007C9B6D /* NCViewerImageDismissAnimationController.swift */; };
-		F79018B7240962C7007C9B6D /* NCViewerImageContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79018B3240962C7007C9B6D /* NCViewerImageContentView.swift */; };
-		F79018B8240962C7007C9B6D /* NCViewerImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79018B4240962C7007C9B6D /* NCViewerImageViewController.swift */; };
-		F79018B9240962C7007C9B6D /* NCViewerImageContentTransformers.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79018B5240962C7007C9B6D /* NCViewerImageContentTransformers.swift */; };
 		F79630EE215527D40015EEA5 /* NCViewerVideoCommon.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79630ED215527D40015EEA5 /* NCViewerVideoCommon.swift */; };
 		F79728D422F96F2E003CACA7 /* NCShareLinkFolderMenuView.xib in Resources */ = {isa = PBXBuildFile; fileRef = F79728D322F96F2D003CACA7 /* NCShareLinkFolderMenuView.xib */; };
 		F79728D622F9A0B1003CACA7 /* NCShareUserFolderMenuView.xib in Resources */ = {isa = PBXBuildFile; fileRef = F79728D522F9A0B0003CACA7 /* NCShareUserFolderMenuView.xib */; };
@@ -379,6 +380,12 @@
 		F7063DEC2199E55F003F38DA /* SVGKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SVGKit.framework; path = Carthage/Build/iOS/SVGKit.framework; sourceTree = "<group>"; };
 		F7063DEE2199E568003F38DA /* CocoaLumberjack.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CocoaLumberjack.framework; path = Carthage/Build/iOS/CocoaLumberjack.framework; sourceTree = "<group>"; };
 		F7063DF02199E56E003F38DA /* CocoaLumberjackSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CocoaLumberjackSwift.framework; path = Carthage/Build/iOS/CocoaLumberjackSwift.framework; sourceTree = "<group>"; };
+		F70753E02542A99100972D44 /* NCZoomTransitionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCZoomTransitionController.swift; sourceTree = "<group>"; };
+		F70753E12542A99100972D44 /* NCZoomDismissalInteractionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCZoomDismissalInteractionController.swift; sourceTree = "<group>"; };
+		F70753E22542A99100972D44 /* NCZoomAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCZoomAnimator.swift; sourceTree = "<group>"; };
+		F70753EA2542A99800972D44 /* NCPhotoPageContainerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCPhotoPageContainerViewController.swift; sourceTree = "<group>"; };
+		F70753F02542A9A200972D44 /* NCPhotoZoomViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCPhotoZoomViewController.swift; sourceTree = "<group>"; };
+		F70753F62542A9C000972D44 /* NCViewerImage.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCViewerImage.storyboard; sourceTree = "<group>"; };
 		F707C26421A2DC5200F6181E /* NCStoreReview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCStoreReview.swift; sourceTree = "<group>"; };
 		F70968A324212C4E00ED60E5 /* NCLivePhoto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCLivePhoto.swift; sourceTree = "<group>"; };
 		F70A07C8205285FB00DC1231 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = "<group>"; };
@@ -430,7 +437,6 @@
 		F733B65121997CC1001C1FFA /* TLPhotoPicker.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TLPhotoPicker.framework; path = Carthage/Build/iOS/TLPhotoPicker.framework; sourceTree = "<group>"; };
 		F7362A1E220C853A005101B5 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
 		F736B551234DCF57008A5C9F /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = Carthage/Build/iOS/Alamofire.framework; sourceTree = "<group>"; };
-		F7381439240FE48D00404AC7 /* NCViewerImageCommon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCViewerImageCommon.swift; sourceTree = "<group>"; };
 		F7381EDA218218C9000B1560 /* NCOffline.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCOffline.swift; sourceTree = "<group>"; };
 		F7381EDE218218C9000B1560 /* NCOffline.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCOffline.storyboard; sourceTree = "<group>"; };
 		F738E8401F90FFD100F95C8E /* NCManageEndToEndEncryption.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NCManageEndToEndEncryption.h; sourceTree = "<group>"; };
@@ -585,10 +591,6 @@
 		F78F74352163781100C2ADAD /* NCTrash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCTrash.swift; sourceTree = "<group>"; };
 		F790110D21415BF600D7B136 /* NCViewerRichdocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCViewerRichdocument.swift; sourceTree = "<group>"; };
 		F79018A424092EF4007C9B6D /* ATGMediaBrowser.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ATGMediaBrowser.framework; path = Carthage/Build/iOS/ATGMediaBrowser.framework; sourceTree = "<group>"; };
-		F79018B2240962C7007C9B6D /* NCViewerImageDismissAnimationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCViewerImageDismissAnimationController.swift; sourceTree = "<group>"; };
-		F79018B3240962C7007C9B6D /* NCViewerImageContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCViewerImageContentView.swift; sourceTree = "<group>"; };
-		F79018B4240962C7007C9B6D /* NCViewerImageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCViewerImageViewController.swift; sourceTree = "<group>"; };
-		F79018B5240962C7007C9B6D /* NCViewerImageContentTransformers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCViewerImageContentTransformers.swift; sourceTree = "<group>"; };
 		F7956FC91B4886E60085DEA3 /* CCUploadFromOtherUpp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCUploadFromOtherUpp.h; sourceTree = "<group>"; };
 		F7956FCA1B4886E60085DEA3 /* CCUploadFromOtherUpp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCUploadFromOtherUpp.m; sourceTree = "<group>"; };
 		F7956FCB1B4886E60085DEA3 /* CCUploadFromOtherUpp.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = CCUploadFromOtherUpp.storyboard; sourceTree = "<group>"; };
@@ -833,6 +835,16 @@
 			path = Imagemeter;
 			sourceTree = "<group>";
 		};
+		F70753DF2542A99100972D44 /* Animation */ = {
+			isa = PBXGroup;
+			children = (
+				F70753E02542A99100972D44 /* NCZoomTransitionController.swift */,
+				F70753E12542A99100972D44 /* NCZoomDismissalInteractionController.swift */,
+				F70753E22542A99100972D44 /* NCZoomAnimator.swift */,
+			);
+			path = Animation;
+			sourceTree = "<group>";
+		};
 		F70784811A2C8A0D00AC9FFF /* UploadFromOtherUpp */ = {
 			isa = PBXGroup;
 			children = (
@@ -1113,11 +1125,10 @@
 		F79018B1240962C7007C9B6D /* NCViewerImage */ = {
 			isa = PBXGroup;
 			children = (
-				F79018B2240962C7007C9B6D /* NCViewerImageDismissAnimationController.swift */,
-				F79018B3240962C7007C9B6D /* NCViewerImageContentView.swift */,
-				F7381439240FE48D00404AC7 /* NCViewerImageCommon.swift */,
-				F79018B4240962C7007C9B6D /* NCViewerImageViewController.swift */,
-				F79018B5240962C7007C9B6D /* NCViewerImageContentTransformers.swift */,
+				F70753F62542A9C000972D44 /* NCViewerImage.storyboard */,
+				F70753EA2542A99800972D44 /* NCPhotoPageContainerViewController.swift */,
+				F70753F02542A9A200972D44 /* NCPhotoZoomViewController.swift */,
+				F70753DF2542A99100972D44 /* Animation */,
 			);
 			path = NCViewerImage;
 			sourceTree = "<group>";
@@ -1894,6 +1905,7 @@
 				F7725A61251F33BB00D125E0 /* NCFiles.storyboard in Resources */,
 				F700510122DF63AC003A3356 /* NCShare.storyboard in Resources */,
 				F787704F22E7019900F287A9 /* NCShareLinkCell.xib in Resources */,
+				F70753F72542A9C000972D44 /* NCViewerImage.storyboard in Resources */,
 				F70A58C024D0545100DED00D /* NCCapabilitiesViewController.storyboard in Resources */,
 				F749C10D23C4A5340027D966 /* NCIntro.storyboard in Resources */,
 				F7239877253D86D300257F49 /* NCEmptyView.xib in Resources */,
@@ -2045,6 +2057,7 @@
 				F77B0DF21D118A16002130FE /* CCUploadFromOtherUpp.m in Sources */,
 				F77444F522281649000D5EB0 /* NCGridMediaCell.swift in Sources */,
 				F7AE00F8230E81CB007ACF8A /* NCBrowserWeb.swift in Sources */,
+				F70753E52542A99100972D44 /* NCZoomAnimator.swift in Sources */,
 				F70A58BE24D0349500DED00D /* NCCapabilitiesViewController.swift in Sources */,
 				370D26AF248A3D7A00121797 /* NCImageCellProtocol.swift in Sources */,
 				F77B0DF51D118A16002130FE /* CCUtility.m in Sources */,
@@ -2053,12 +2066,10 @@
 				F78ACD4021903CC20088454D /* NCGridCell.swift in Sources */,
 				37ECC83B23D0C7410082EFA2 /* NCMenuAction.swift in Sources */,
 				F75B0ABD244C4DBB00E58DCA /* NCNetworkingNotificationCenter.swift in Sources */,
-				F79018B8240962C7007C9B6D /* NCViewerImageViewController.swift in Sources */,
 				F769454022E9F077000A798A /* NCSharePaging.swift in Sources */,
 				F78ACD4221903CE00088454D /* NCListCell.swift in Sources */,
 				F76D3CF12428B40E005DFA87 /* NCViewerPDFSearch.swift in Sources */,
 				F73F537F1E929C8500F8678D /* NCMore.swift in Sources */,
-				F79018B7240962C7007C9B6D /* NCViewerImageContentView.swift in Sources */,
 				F78E7065219F096B006F23E4 /* NCAvatar.swift in Sources */,
 				F7DFB7F0219C5B8000680748 /* NCCreateFormUploadAssets.swift in Sources */,
 				F73B422C2476764F00A30FD3 /* NCNotification.swift in Sources */,
@@ -2068,6 +2079,7 @@
 				F769454622E9F1B0000A798A /* NCShareCommon.swift in Sources */,
 				F7B0C1751EE839A30033AC24 /* NCAutoUpload.m in Sources */,
 				F738E8421F90FFD100F95C8E /* NCManageEndToEndEncryption.m in Sources */,
+				F70753F12542A9A200972D44 /* NCPhotoZoomViewController.swift in Sources */,
 				F7A80BCB252624C100C7CD01 /* NCFileViewInFolder.swift in Sources */,
 				F78A18B823CDE2B300F681F3 /* NCViewerRichWorkspace.swift in Sources */,
 				F78A18B623CDD07D00F681F3 /* NCViewerRichWorkspaceWebView.swift in Sources */,
@@ -2088,6 +2100,7 @@
 				F700510522DF6A89003A3356 /* NCShare.swift in Sources */,
 				F7CA1ED120E7E3FE002CC65E /* PKCircleProgressView.m in Sources */,
 				F72D1007210B6882009C96B7 /* NCPushNotificationEncryption.m in Sources */,
+				F70753E42542A99100972D44 /* NCZoomDismissalInteractionController.swift in Sources */,
 				F7CA1ED420E7E3FE002CC65E /* PKPendingView.m in Sources */,
 				F769454222E9F0EE000A798A /* NCShareLinkMenuView.swift in Sources */,
 				F785EE9D246196DF00B3F945 /* NCNetworkingE2EE.swift in Sources */,
@@ -2112,7 +2125,6 @@
 				F7239871253D86B600257F49 /* NCEmptyDataSet.swift in Sources */,
 				F7F878AE1FB9E3B900599E4F /* NCEndToEndMetadata.swift in Sources */,
 				F7DBC37C23325E02001A85BA /* NCAppConfigView.swift in Sources */,
-				F738143A240FE48D00404AC7 /* NCViewerImageCommon.swift in Sources */,
 				3781B9B023DB2B7E006B4B1D /* AppDelegate+Menu.swift in Sources */,
 				F73D5E47246DE09200DF6467 /* NCElementsJSON.swift in Sources */,
 				F710D1F52405770F00A6033D /* NCViewerPDF.swift in Sources */,
@@ -2137,19 +2149,19 @@
 				F7DFB7F4219C5CA800680748 /* NCCreateFormUploadScanDocument.swift in Sources */,
 				F70006FC2416500B00F214A5 /* NCViewerVideo.swift in Sources */,
 				F710D1FF24057E5E00A6033D /* NCActionSheetHeaderView.swift in Sources */,
-				F79018B6240962C7007C9B6D /* NCViewerImageDismissAnimationController.swift in Sources */,
 				F7020FCE2233D7F700B7297D /* NCCreateFormUploadVoiceNote.swift in Sources */,
+				F70753E32542A99100972D44 /* NCZoomTransitionController.swift in Sources */,
 				F7F4B1D823C74B3E00D82A6E /* NCRichWorkspace.swift in Sources */,
 				F726EEEC1FED1C820030B9C8 /* NCEndToEndInitialize.swift in Sources */,
 				F79A65C62191D95E00FF6DCC /* NCSelect.swift in Sources */,
 				F7E0E1DC22327885006B0911 /* NCAudioRecorderViewController.swift in Sources */,
 				F70CAE3A1F8CF31A008125FD /* NCEndToEndEncryption.m in Sources */,
+				F70753EB2542A99800972D44 /* NCPhotoPageContainerViewController.swift in Sources */,
 				F74C0436253F1CDC009762AB /* NCShares.swift in Sources */,
 				F7AE00F5230D5F9E007ACF8A /* NCLoginWeb.swift in Sources */,
 				F7B2DEF01F976854007CF4D2 /* NYMnemonic.m in Sources */,
 				F707C26521A2DC5200F6181E /* NCStoreReview.swift in Sources */,
 				F7BAADCB1ED5A87C00B7EAD4 /* NCManageDatabase.swift in Sources */,
-				F79018B9240962C7007C9B6D /* NCViewerImageContentTransformers.swift in Sources */,
 				F70968A424212C4E00ED60E5 /* NCLivePhoto.swift in Sources */,
 				F704FA5C232A343F00BBA952 /* IMImagemeterViewer.swift in Sources */,
 				F7CA1ED220E7E3FE002CC65E /* PKCircleView.m in Sources */,

+ 83 - 0
iOSClient/Main/Colleaction Common/NCCollectionViewCommon.swift

@@ -68,6 +68,11 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
     
     internal var isReloadDataSourceNetworkInProgress: Bool = false
     
+    var photos: [UIImage] = []
+    var selectedIndexPath: IndexPath!
+    var currentLeftSafeAreaInset: CGFloat = 0.0
+    var currentRightSafeAreaInset: CGFloat = 0.0
+    
     // DECLARE
     internal var layoutKey = ""
     internal var titleCurrentFolder = ""
@@ -190,6 +195,12 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
         
         reloadDataSourceNetwork()
     }
+    
+    override func viewSafeAreaInsetsDidChange() {
+    
+        currentLeftSafeAreaInset = view.safeAreaInsets.left
+        currentRightSafeAreaInset = view.safeAreaInsets.right
+    }
         
     func presentationControllerDidDismiss( _ presentationController: UIPresentationController) {
         let viewController = presentationController.presentedViewController
@@ -1043,6 +1054,7 @@ extension NCCollectionViewCommon: UICollectionViewDelegate {
         
         guard let metadata = dataSource.cellForItemAt(indexPath: indexPath) else { return }
         metadataTouch = metadata
+        selectedIndexPath = indexPath
         
         if isEditMode {
             if let index = selectOcId.firstIndex(of: metadata.ocId) {
@@ -1393,6 +1405,9 @@ extension NCCollectionViewCommon: UICollectionViewDataSource {
                         }
                     }
                 }
+                if cell.imageItem.image != nil {
+                    self.photos.append(cell.imageItem.image!)
+                }
                 
                 cell.labelInfo.text = CCUtility.dateDiff(metadata.date as Date) + " · " + CCUtility.transformedSize(metadata.size)
                                 
@@ -1576,6 +1591,9 @@ extension NCCollectionViewCommon: UICollectionViewDataSource {
                         }
                     }
                 }
+                if cell.imageItem.image != nil {
+                    self.photos.append(cell.imageItem.image!)
+                }
                 
                 // image Local
                 if dataSource.metadataOffLine.contains(metadata.ocId) {
@@ -1644,3 +1662,68 @@ extension NCCollectionViewCommon: UICollectionViewDelegateFlowLayout {
         return CGSize(width: collectionView.frame.width, height: footerHeight)
     }
 }
+
+extension NCCollectionViewCommon: PhotoPageContainerViewControllerDelegate {
+ 
+    func containerViewController(_ containerViewController: PhotoPageContainerViewController, indexDidUpdate currentIndex: Int) {
+        self.selectedIndexPath = IndexPath(row: currentIndex, section: 0)
+        self.collectionView.scrollToItem(at: self.selectedIndexPath, at: .centeredVertically, animated: false)
+    }
+}
+
+extension NCCollectionViewCommon: ZoomAnimatorDelegate {
+    
+    func transitionWillStartWith(zoomAnimator: ZoomAnimator) {
+        
+    }
+    
+    func transitionDidEndWith(zoomAnimator: ZoomAnimator) {
+        
+        var cellFrame: CGRect = CGRect.init()
+        let cell = self.collectionView.cellForItem(at: self.selectedIndexPath)
+
+        if cell is NCListCell {
+            cellFrame = self.collectionView.convert((cell as! NCListCell).frame, to: self.view)
+        } else if cell is NCGridCell {
+            cellFrame = self.collectionView.convert((cell as! NCGridCell).frame, to: self.view)
+        }
+        
+        if cellFrame.minY < self.collectionView.contentInset.top {
+            self.collectionView.scrollToItem(at: self.selectedIndexPath, at: .top, animated: false)
+        } else if cellFrame.maxY > self.view.frame.height - self.collectionView.contentInset.bottom {
+            self.collectionView.scrollToItem(at: self.selectedIndexPath, at: .bottom, animated: false)
+        }
+    }
+    
+    func referenceImageView(for zoomAnimator: ZoomAnimator) -> UIImageView? {
+        
+        let cell = collectionView.cellForItem(at: selectedIndexPath)
+        if cell is NCListCell {
+            return (cell as! NCListCell).imageItem
+        } else if cell is NCGridCell {
+            return (cell as! NCGridCell).imageItem
+        }
+        
+        return nil
+    }
+    
+    func referenceImageViewFrameInTransitioningView(for zoomAnimator: ZoomAnimator) -> CGRect? {
+        
+        /*
+        self.view.layoutIfNeeded()
+        self.collectionView.layoutIfNeeded()
+        
+        let unconvertedFrame = getFrameFromCollectionViewCell(for: self.selectedIndexPath)        
+        let cellFrame = self.collectionView.convert(unconvertedFrame, to: self.view)
+        
+        if cellFrame.minY < self.collectionView.contentInset.top {
+            return CGRect(x: cellFrame.minX, y: self.collectionView.contentInset.top, width: cellFrame.width, height: cellFrame.height - (self.collectionView.contentInset.top - cellFrame.minY))
+        }
+        
+        return cellFrame
+        */
+        return nil
+    }
+    
+}
+

+ 18 - 1
iOSClient/Viewer/NCViewer.swift

@@ -34,7 +34,6 @@ class NCViewer: NSObject {
     private var viewerQuickLook: NCViewerQuickLook?
     private var metadata = tableMetadata()
     private var metadatas: [tableMetadata] = []
-    private var viewerImageViewController: NCViewerImageViewController?
 
     func view(viewController: UIViewController, metadata: tableMetadata, metadatas: [tableMetadata]? = nil) {
 
@@ -59,6 +58,24 @@ class NCViewer: NSObject {
         // IMAGE
         if metadata.typeFile == k_metadataTypeFile_image {
             
+            if let navigationController = getPushNavigationController(viewController: viewController, serverUrl: metadata.serverUrl) {
+                
+                let vcImageView:PhotoPageContainerViewController = UIStoryboard(name: "NCViewerImage", bundle: nil).instantiateInitialViewController() as! PhotoPageContainerViewController
+            
+                navigationController.delegate = vcImageView.transitionController
+                vcImageView.transitionController.toDelegate = vcImageView
+                if viewController is NCFiles {
+                    let viewController: NCFiles = viewController as! NCFiles
+                    vcImageView.transitionController.fromDelegate = viewController
+                    vcImageView.transitionController.toDelegate = vcImageView
+                    vcImageView.delegate = viewController
+                    vcImageView.currentIndex = viewController.selectedIndexPath.row
+                    vcImageView.photos = viewController.photos
+                    
+                    navigationController.pushViewController(vcImageView, animated: true)
+                }
+            }
+    
             return
         }
         

+ 166 - 0
iOSClient/Viewer/NCViewerImage/Animation/NCZoomAnimator.swift

@@ -0,0 +1,166 @@
+
+import UIKit
+
+protocol ZoomAnimatorDelegate: class {
+    func transitionWillStartWith(zoomAnimator: ZoomAnimator)
+    func transitionDidEndWith(zoomAnimator: ZoomAnimator)
+    func referenceImageView(for zoomAnimator: ZoomAnimator) -> UIImageView?
+    func referenceImageViewFrameInTransitioningView(for zoomAnimator: ZoomAnimator) -> CGRect?
+}
+
+class ZoomAnimator: NSObject {
+    
+    weak var fromDelegate: ZoomAnimatorDelegate?
+    weak var toDelegate: ZoomAnimatorDelegate?
+
+    var transitionImageView: UIImageView?
+    var isPresenting: Bool = true
+    
+    fileprivate func animateZoomInTransition(using transitionContext: UIViewControllerContextTransitioning) {
+        
+        let containerView = transitionContext.containerView
+        
+        guard let toVC = transitionContext.viewController(forKey: .to),
+            let fromVC = transitionContext.viewController(forKey: .from),
+            let fromReferenceImageView = self.fromDelegate?.referenceImageView(for: self),
+            let toReferenceImageView = self.toDelegate?.referenceImageView(for: self),
+            let fromReferenceImageViewFrame = self.fromDelegate?.referenceImageViewFrameInTransitioningView(for: self)
+            else {
+                return
+        }
+        
+        self.fromDelegate?.transitionWillStartWith(zoomAnimator: self)
+        self.toDelegate?.transitionWillStartWith(zoomAnimator: self)
+        
+        toVC.view.alpha = 0
+        toReferenceImageView.isHidden = true
+        containerView.addSubview(toVC.view)
+        
+        let referenceImage = fromReferenceImageView.image!
+        
+        if self.transitionImageView == nil {
+            let transitionImageView = UIImageView(image: referenceImage)
+            transitionImageView.contentMode = .scaleAspectFill
+            transitionImageView.clipsToBounds = true
+            transitionImageView.frame = fromReferenceImageViewFrame
+            self.transitionImageView = transitionImageView
+            containerView.addSubview(transitionImageView)
+        }
+        
+        fromReferenceImageView.isHidden = true
+        
+        let finalTransitionSize = calculateZoomInImageFrame(image: referenceImage, forView: toVC.view)
+        
+        UIView.animate(withDuration: transitionDuration(using: transitionContext),
+                       delay: 0,
+                       usingSpringWithDamping: 0.8,
+                       initialSpringVelocity: 0,
+                       options: [UIView.AnimationOptions.transitionCrossDissolve],
+                       animations: {
+                        self.transitionImageView?.frame = finalTransitionSize
+                        toVC.view.alpha = 1.0
+                        fromVC.tabBarController?.tabBar.alpha = 0
+        },
+                       completion: { completed in
+                    
+                        self.transitionImageView?.removeFromSuperview()
+                        toReferenceImageView.isHidden = false
+                        fromReferenceImageView.isHidden = false
+                        
+                        self.transitionImageView = nil
+                        
+                        transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
+                        self.toDelegate?.transitionDidEndWith(zoomAnimator: self)
+                        self.fromDelegate?.transitionDidEndWith(zoomAnimator: self)
+        })
+    }
+    
+    fileprivate func animateZoomOutTransition(using transitionContext: UIViewControllerContextTransitioning) {
+        let containerView = transitionContext.containerView
+        
+        guard let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to),
+            let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from),
+            let fromReferenceImageView = self.fromDelegate?.referenceImageView(for: self),
+            let toReferenceImageView = self.toDelegate?.referenceImageView(for: self),
+            let fromReferenceImageViewFrame = self.fromDelegate?.referenceImageViewFrameInTransitioningView(for: self),
+            let toReferenceImageViewFrame = self.toDelegate?.referenceImageViewFrameInTransitioningView(for: self)
+            else {
+                return
+        }
+        
+        self.fromDelegate?.transitionWillStartWith(zoomAnimator: self)
+        self.toDelegate?.transitionWillStartWith(zoomAnimator: self)
+        
+        toReferenceImageView.isHidden = true
+        
+        let referenceImage = fromReferenceImageView.image!
+        
+        if self.transitionImageView == nil {
+            let transitionImageView = UIImageView(image: referenceImage)
+            transitionImageView.contentMode = .scaleAspectFill
+            transitionImageView.clipsToBounds = true
+            transitionImageView.frame = fromReferenceImageViewFrame
+            self.transitionImageView = transitionImageView
+            containerView.addSubview(transitionImageView)
+        }
+        
+        containerView.insertSubview(toVC.view, belowSubview: fromVC.view)
+        fromReferenceImageView.isHidden = true
+        
+        let finalTransitionSize = toReferenceImageViewFrame
+        
+        UIView.animate(withDuration: transitionDuration(using: transitionContext),
+                       delay: 0,
+                       options: [],
+                       animations: {
+                        fromVC.view.alpha = 0
+                        self.transitionImageView?.frame = finalTransitionSize
+                        toVC.tabBarController?.tabBar.alpha = 1
+        }, completion: { completed in
+            
+            self.transitionImageView?.removeFromSuperview()
+            toReferenceImageView.isHidden = false
+            fromReferenceImageView.isHidden = false
+            
+            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
+            self.toDelegate?.transitionDidEndWith(zoomAnimator: self)
+            self.fromDelegate?.transitionDidEndWith(zoomAnimator: self)
+
+        })
+    }
+    
+    private func calculateZoomInImageFrame(image: UIImage, forView view: UIView) -> CGRect {
+        
+        let viewRatio = view.frame.size.width / view.frame.size.height
+        let imageRatio = image.size.width / image.size.height
+        let touchesSides = (imageRatio > viewRatio)
+        
+        if touchesSides {
+            let height = view.frame.width / imageRatio
+            let yPoint = view.frame.minY + (view.frame.height - height) / 2
+            return CGRect(x: 0, y: yPoint, width: view.frame.width, height: height)
+        } else {
+            let width = view.frame.height * imageRatio
+            let xPoint = view.frame.minX + (view.frame.width - width) / 2
+            return CGRect(x: xPoint, y: 0, width: width, height: view.frame.height)
+        }
+    }
+}
+
+extension ZoomAnimator: UIViewControllerAnimatedTransitioning {
+    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
+        if self.isPresenting {
+            return 0.5
+        } else {
+            return 0.25
+        }
+    }
+    
+    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
+        if self.isPresenting {
+            animateZoomInTransition(using: transitionContext)
+        } else {
+            animateZoomOutTransition(using: transitionContext)
+        }
+    }
+}

+ 164 - 0
iOSClient/Viewer/NCViewerImage/Animation/NCZoomDismissalInteractionController.swift

@@ -0,0 +1,164 @@
+
+
+import UIKit
+
+class ZoomDismissalInteractionController: NSObject {
+    
+    var transitionContext: UIViewControllerContextTransitioning?
+    var animator: UIViewControllerAnimatedTransitioning?
+    
+    var fromReferenceImageViewFrame: CGRect?
+    var toReferenceImageViewFrame: CGRect?
+    
+    func didPanWith(gestureRecognizer: UIPanGestureRecognizer) {
+        
+        guard let transitionContext = self.transitionContext,
+            let animator = self.animator as? ZoomAnimator,
+            let transitionImageView = animator.transitionImageView,
+            let fromVC = transitionContext.viewController(forKey: .from),
+            let toVC = transitionContext.viewController(forKey: .to),
+            let fromReferenceImageView = animator.fromDelegate?.referenceImageView(for: animator),
+            let toReferenceImageView = animator.toDelegate?.referenceImageView(for: animator),
+            let fromReferenceImageViewFrame = self.fromReferenceImageViewFrame,
+            let toReferenceImageViewFrame = self.toReferenceImageViewFrame else {
+                return
+        }
+        
+        
+        fromReferenceImageView.isHidden = true
+        
+        let anchorPoint = CGPoint(x: fromReferenceImageViewFrame.midX, y: fromReferenceImageViewFrame.midY)
+        let translatedPoint = gestureRecognizer.translation(in: fromReferenceImageView)
+        let verticalDelta : CGFloat = translatedPoint.y < 0 ? 0 : translatedPoint.y
+
+        let backgroundAlpha = backgroundAlphaFor(view: fromVC.view, withPanningVerticalDelta: verticalDelta)
+        let scale = scaleFor(view: fromVC.view, withPanningVerticalDelta: verticalDelta)
+        
+        fromVC.view.alpha = backgroundAlpha
+        
+        transitionImageView.transform = CGAffineTransform(scaleX: scale, y: scale)
+        let newCenter = CGPoint(x: anchorPoint.x + translatedPoint.x, y: anchorPoint.y + translatedPoint.y - transitionImageView.frame.height * (1 - scale) / 2.0)
+        transitionImageView.center = newCenter
+        
+        toReferenceImageView.isHidden = true
+        
+        transitionContext.updateInteractiveTransition(1 - scale)
+        
+        toVC.tabBarController?.tabBar.alpha = 1 - backgroundAlpha
+        
+        if gestureRecognizer.state == .ended {
+            
+            let velocity = gestureRecognizer.velocity(in: fromVC.view)
+            if velocity.y < 0 || newCenter.y < anchorPoint.y {
+                
+                //cancel
+                UIView.animate(
+                    withDuration: 0.5,
+                    delay: 0,
+                    usingSpringWithDamping: 0.9,
+                    initialSpringVelocity: 0,
+                    options: [],
+                    animations: {
+                        transitionImageView.frame = fromReferenceImageViewFrame
+                        fromVC.view.alpha = 1.0
+                        toVC.tabBarController?.tabBar.alpha = 0
+                },
+                    completion: { completed in
+                        
+                        toReferenceImageView.isHidden = false
+                        fromReferenceImageView.isHidden = false
+                        transitionImageView.removeFromSuperview()
+                        animator.transitionImageView = nil
+                        transitionContext.cancelInteractiveTransition()
+                        transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
+                        animator.toDelegate?.transitionDidEndWith(zoomAnimator: animator)
+                        animator.fromDelegate?.transitionDidEndWith(zoomAnimator: animator)
+                        self.transitionContext = nil
+                })
+                return
+            }
+            
+            //start animation
+            let finalTransitionSize = toReferenceImageViewFrame
+            
+            UIView.animate(withDuration: 0.25,
+                           delay: 0,
+                           options: [],
+                           animations: {
+                            fromVC.view.alpha = 0
+                            transitionImageView.frame = finalTransitionSize
+                            toVC.tabBarController?.tabBar.alpha = 1
+                            
+            }, completion: { completed in
+                
+                transitionImageView.removeFromSuperview()
+                toReferenceImageView.isHidden = false
+                fromReferenceImageView.isHidden = false
+                
+                self.transitionContext?.finishInteractiveTransition()
+                transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
+                animator.toDelegate?.transitionDidEndWith(zoomAnimator: animator)
+                animator.fromDelegate?.transitionDidEndWith(zoomAnimator: animator)
+                self.transitionContext = nil
+            })
+        }
+    }
+    
+    func backgroundAlphaFor(view: UIView, withPanningVerticalDelta verticalDelta: CGFloat) -> CGFloat {
+        let startingAlpha:CGFloat = 1.0
+        let finalAlpha: CGFloat = 0.0
+        let totalAvailableAlpha = startingAlpha - finalAlpha
+        
+        let maximumDelta = view.bounds.height / 4.0
+        let deltaAsPercentageOfMaximun = min(abs(verticalDelta) / maximumDelta, 1.0)
+        
+        return startingAlpha - (deltaAsPercentageOfMaximun * totalAvailableAlpha)
+    }
+    
+    func scaleFor(view: UIView, withPanningVerticalDelta verticalDelta: CGFloat) -> CGFloat {
+        let startingScale:CGFloat = 1.0
+        let finalScale: CGFloat = 0.5
+        let totalAvailableScale = startingScale - finalScale
+        
+        let maximumDelta = view.bounds.height / 2.0
+        let deltaAsPercentageOfMaximun = min(abs(verticalDelta) / maximumDelta, 1.0)
+        
+        return startingScale - (deltaAsPercentageOfMaximun * totalAvailableScale)
+    }
+}
+
+extension ZoomDismissalInteractionController: UIViewControllerInteractiveTransitioning {
+    func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) {
+        self.transitionContext = transitionContext
+        
+        let containerView = transitionContext.containerView
+        
+        guard let animator = self.animator as? ZoomAnimator,
+            let fromVC = transitionContext.viewController(forKey: .from),
+            let toVC = transitionContext.viewController(forKey: .to),
+            let fromReferenceImageViewFrame = animator.fromDelegate?.referenceImageViewFrameInTransitioningView(for: animator),
+            let toReferenceImageViewFrame = animator.toDelegate?.referenceImageViewFrameInTransitioningView(for: animator),
+            let fromReferenceImageView = animator.fromDelegate?.referenceImageView(for: animator)
+            else {
+                return
+        }
+        
+        animator.fromDelegate?.transitionWillStartWith(zoomAnimator: animator)
+        animator.toDelegate?.transitionWillStartWith(zoomAnimator: animator)
+        
+        self.fromReferenceImageViewFrame = fromReferenceImageViewFrame
+        self.toReferenceImageViewFrame = toReferenceImageViewFrame
+        
+        let referenceImage = fromReferenceImageView.image!
+        
+        containerView.insertSubview(toVC.view, belowSubview: fromVC.view)
+        if animator.transitionImageView == nil {
+            let transitionImageView = UIImageView(image: referenceImage)
+            transitionImageView.contentMode = .scaleAspectFill
+            transitionImageView.clipsToBounds = true
+            transitionImageView.frame = fromReferenceImageViewFrame
+            animator.transitionImageView = transitionImageView
+            containerView.addSubview(transitionImageView)
+        }
+    }
+}

+ 79 - 0
iOSClient/Viewer/NCViewerImage/Animation/NCZoomTransitionController.swift

@@ -0,0 +1,79 @@
+
+
+import UIKit
+
+class ZoomTransitionController: NSObject {
+    
+    let animator: ZoomAnimator
+    let interactionController: ZoomDismissalInteractionController
+    var isInteractive: Bool = false
+
+    weak var fromDelegate: ZoomAnimatorDelegate?
+    weak var toDelegate: ZoomAnimatorDelegate?
+    
+    override init() {
+        animator = ZoomAnimator()
+        interactionController = ZoomDismissalInteractionController()
+        super.init()
+    }
+    
+    func didPanWith(gestureRecognizer: UIPanGestureRecognizer) {
+        self.interactionController.didPanWith(gestureRecognizer: gestureRecognizer)
+    }
+}
+
+extension ZoomTransitionController: UIViewControllerTransitioningDelegate {
+    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
+        self.animator.isPresenting = true
+        self.animator.fromDelegate = fromDelegate
+        self.animator.toDelegate = toDelegate
+        return self.animator
+    }
+    
+    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
+        self.animator.isPresenting = false
+        let tmp = self.fromDelegate
+        self.animator.fromDelegate = self.toDelegate
+        self.animator.toDelegate = tmp
+        return self.animator
+    }
+
+    func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
+        if !self.isInteractive {
+            return nil
+        }
+        
+        self.interactionController.animator = animator
+        return self.interactionController
+    }
+
+}
+
+extension ZoomTransitionController: UINavigationControllerDelegate {
+    func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
+        
+        if operation == .push {
+            self.animator.isPresenting = true
+            self.animator.fromDelegate = fromDelegate
+            self.animator.toDelegate = toDelegate
+        } else {
+            self.animator.isPresenting = false
+            let tmp = self.fromDelegate
+            self.animator.fromDelegate = self.toDelegate
+            self.animator.toDelegate = tmp
+        }
+        
+        return self.animator
+    }
+    
+    func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
+        
+        if !self.isInteractive {
+            return nil
+        }
+        
+        self.interactionController.animator = animator
+        return self.interactionController
+    }
+
+}

+ 233 - 0
iOSClient/Viewer/NCViewerImage/NCPhotoPageContainerViewController.swift

@@ -0,0 +1,233 @@
+
+
+import UIKit
+
+protocol PhotoPageContainerViewControllerDelegate: class {
+    func containerViewController(_ containerViewController: PhotoPageContainerViewController, indexDidUpdate currentIndex: Int)
+}
+
+class PhotoPageContainerViewController: UIViewController, UIGestureRecognizerDelegate {
+
+    enum ScreenMode {
+        case full, normal
+    }
+    var currentMode: ScreenMode = .normal
+    
+    weak var delegate: PhotoPageContainerViewControllerDelegate?
+    
+    var pageViewController: UIPageViewController {
+        return self.children[0] as! UIPageViewController
+    }
+    
+    var currentViewController: PhotoZoomViewController {
+        return self.pageViewController.viewControllers![0] as! PhotoZoomViewController
+    }
+    
+    var photos: [UIImage]!
+    var currentIndex = 0
+    var nextIndex: Int?
+    
+    var panGestureRecognizer: UIPanGestureRecognizer!
+    var singleTapGestureRecognizer: UITapGestureRecognizer!
+    
+    var transitionController = ZoomTransitionController()
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        self.pageViewController.delegate = self
+        self.pageViewController.dataSource = self
+        self.panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(didPanWith(gestureRecognizer:)))
+        self.panGestureRecognizer.delegate = self
+        self.pageViewController.view.addGestureRecognizer(self.panGestureRecognizer)
+        
+        self.singleTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didSingleTapWith(gestureRecognizer:)))
+        self.pageViewController.view.addGestureRecognizer(self.singleTapGestureRecognizer)
+        
+        let vc = UIStoryboard(name: "NCViewerImage", bundle: nil).instantiateViewController(withIdentifier: "\(PhotoZoomViewController.self)") as! PhotoZoomViewController
+        vc.delegate = self
+        vc.index = self.currentIndex
+        vc.image = self.photos[self.currentIndex]
+        self.singleTapGestureRecognizer.require(toFail: vc.doubleTapGestureRecognizer)
+        let viewControllers = [
+            vc
+        ]
+        
+        self.pageViewController.setViewControllers(viewControllers, direction: .forward, animated: true, completion: nil)
+    }
+    
+    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
+        
+        if let gestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer {
+            let velocity = gestureRecognizer.velocity(in: self.view)
+            
+            var velocityCheck : Bool = false
+            
+            if UIDevice.current.orientation.isLandscape {
+                velocityCheck = velocity.x < 0
+            }
+            else {
+                velocityCheck = velocity.y < 0
+            }
+            if velocityCheck {
+                return false
+            }
+        }
+        
+        return true
+    }
+    
+    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
+        
+        if otherGestureRecognizer == self.currentViewController.scrollView.panGestureRecognizer {
+            if self.currentViewController.scrollView.contentOffset.y == 0 {
+                return true
+            }
+        }
+        
+        return false
+    }
+    
+    override func didReceiveMemoryWarning() {
+        super.didReceiveMemoryWarning()
+        // Dispose of any resources that can be recreated.
+    }
+    
+    @objc func didPanWith(gestureRecognizer: UIPanGestureRecognizer) {
+        switch gestureRecognizer.state {
+        case .began:
+            self.currentViewController.scrollView.isScrollEnabled = false
+            self.transitionController.isInteractive = true
+            let _ = self.navigationController?.popViewController(animated: true)
+        case .ended:
+            if self.transitionController.isInteractive {
+                self.currentViewController.scrollView.isScrollEnabled = true
+                self.transitionController.isInteractive = false
+                self.transitionController.didPanWith(gestureRecognizer: gestureRecognizer)
+            }
+        default:
+            if self.transitionController.isInteractive {
+                self.transitionController.didPanWith(gestureRecognizer: gestureRecognizer)
+            }
+        }
+    }
+    
+    @objc func didSingleTapWith(gestureRecognizer: UITapGestureRecognizer) {
+        if self.currentMode == .full {
+            changeScreenMode(to: .normal)
+            self.currentMode = .normal
+        } else {
+            changeScreenMode(to: .full)
+            self.currentMode = .full
+        }
+
+    }
+    
+    func changeScreenMode(to: ScreenMode) {
+        if to == .full {
+            self.navigationController?.setNavigationBarHidden(true, animated: false)
+            UIView.animate(withDuration: 0.25,
+                           animations: {
+                            self.view.backgroundColor = .black
+                            
+            }, completion: { completed in
+            })
+        } else {
+            self.navigationController?.setNavigationBarHidden(false, animated: false)
+            UIView.animate(withDuration: 0.25,
+                           animations: {
+                            if #available(iOS 13.0, *) {
+                                self.view.backgroundColor = .systemBackground
+                            } else {
+                                self.view.backgroundColor = .white
+                            }
+            }, completion: { completed in
+            })
+        }
+    }
+}
+
+extension PhotoPageContainerViewController: UIPageViewControllerDelegate, UIPageViewControllerDataSource {
+    
+    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
+        
+        if currentIndex == 0 {
+            return nil
+        }
+        
+        let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "\(PhotoZoomViewController.self)") as! PhotoZoomViewController
+        vc.delegate = self
+        vc.image = self.photos[currentIndex - 1]
+        vc.index = currentIndex - 1
+        self.singleTapGestureRecognizer.require(toFail: vc.doubleTapGestureRecognizer)
+        return vc
+        
+    }
+    
+    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
+        
+        if currentIndex == (self.photos.count - 1) {
+            return nil
+        }
+        
+        let vc = UIStoryboard(name: "NCViewerImage", bundle: nil).instantiateViewController(withIdentifier: "\(PhotoZoomViewController.self)") as! PhotoZoomViewController
+        vc.delegate = self
+        self.singleTapGestureRecognizer.require(toFail: vc.doubleTapGestureRecognizer)
+        vc.image = self.photos[currentIndex + 1]
+        vc.index = currentIndex + 1
+        return vc
+        
+    }
+    
+    func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
+        
+        guard let nextVC = pendingViewControllers.first as? PhotoZoomViewController else {
+            return
+        }
+        
+        self.nextIndex = nextVC.index
+    }
+    
+    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
+        
+        if (completed && self.nextIndex != nil) {
+            previousViewControllers.forEach { vc in
+                let zoomVC = vc as! PhotoZoomViewController
+                zoomVC.scrollView.zoomScale = zoomVC.scrollView.minimumZoomScale
+            }
+
+            self.currentIndex = self.nextIndex!
+            self.delegate?.containerViewController(self, indexDidUpdate: self.currentIndex)
+        }
+        
+        self.nextIndex = nil
+    }
+    
+}
+
+extension PhotoPageContainerViewController: PhotoZoomViewControllerDelegate {
+    
+    func photoZoomViewController(_ photoZoomViewController: PhotoZoomViewController, scrollViewDidScroll scrollView: UIScrollView) {
+        if scrollView.zoomScale != scrollView.minimumZoomScale && self.currentMode != .full {
+            self.changeScreenMode(to: .full)
+            self.currentMode = .full
+        }
+    }
+}
+
+extension PhotoPageContainerViewController: ZoomAnimatorDelegate {
+    
+    func transitionWillStartWith(zoomAnimator: ZoomAnimator) {
+    }
+    
+    func transitionDidEndWith(zoomAnimator: ZoomAnimator) {
+    }
+    
+    func referenceImageView(for zoomAnimator: ZoomAnimator) -> UIImageView? {
+        return self.currentViewController.imageView
+    }
+    
+    func referenceImageViewFrameInTransitioningView(for zoomAnimator: ZoomAnimator) -> CGRect? {        
+        return self.currentViewController.scrollView.convert(self.currentViewController.imageView.frame, to: self.currentViewController.view)
+    }
+}

+ 137 - 0
iOSClient/Viewer/NCViewerImage/NCPhotoZoomViewController.swift

@@ -0,0 +1,137 @@
+
+import UIKit
+
+protocol PhotoZoomViewControllerDelegate: class {
+    func photoZoomViewController(_ photoZoomViewController: PhotoZoomViewController, scrollViewDidScroll scrollView: UIScrollView)
+}
+
+class PhotoZoomViewController: UIViewController {
+    
+    @IBOutlet weak var imageViewBottomConstraint: NSLayoutConstraint!
+    @IBOutlet weak var imageViewLeadingConstraint: NSLayoutConstraint!
+    @IBOutlet weak var imageViewTopConstraint: NSLayoutConstraint!
+    @IBOutlet weak var imageViewTrailingConstraint: NSLayoutConstraint!
+    @IBOutlet weak var scrollView: UIScrollView!
+    @IBOutlet weak var imageView: UIImageView!
+    
+    weak var delegate: PhotoZoomViewControllerDelegate?
+    
+    var image: UIImage!
+    var index: Int = 0
+
+    var doubleTapGestureRecognizer: UITapGestureRecognizer!
+    
+    required init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+        self.doubleTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didDoubleTapWith(gestureRecognizer:)))
+        self.doubleTapGestureRecognizer.numberOfTapsRequired = 2
+    }
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        self.scrollView.delegate = self
+        if #available(iOS 11, *) {
+            self.scrollView.contentInsetAdjustmentBehavior = .never
+        }
+        self.imageView.image = self.image
+        self.imageView.frame = CGRect(x: self.imageView.frame.origin.x,
+                                      y: self.imageView.frame.origin.y,
+                                      width: self.image.size.width,
+                                      height: self.image.size.height)
+        self.view.addGestureRecognizer(self.doubleTapGestureRecognizer)        
+    }
+    
+    override func viewDidLayoutSubviews() {
+        super.viewDidLayoutSubviews()
+        updateZoomScaleForSize(view.bounds.size)
+        updateConstraintsForSize(view.bounds.size)
+    }
+
+    override func viewDidAppear(_ animated: Bool) {
+        super.viewDidAppear(animated)
+        updateZoomScaleForSize(view.bounds.size)
+        updateConstraintsForSize(view.bounds.size)
+    }
+    
+    override func viewSafeAreaInsetsDidChange() {
+        
+        //When this view's safeAreaInsets change, propagate this information
+        //to the previous ViewController so the collectionView contentInsets
+        //can be updated accordingly. This is necessary in order to properly
+        //calculate the frame position for the dismiss (swipe down) animation
+
+            
+            //Get the parent view controller (ViewController) from the navigation controller
+            guard let parentVC = self.navigationController?.viewControllers.first as? NCFiles else {
+                return
+            }
+            
+            //Update the ViewController's left and right local safeAreaInset variables
+            //with the safeAreaInsets for this current view. These will be used to
+            //update the contentInsets of the collectionView inside ViewController
+            parentVC.currentLeftSafeAreaInset = self.view.safeAreaInsets.left
+            parentVC.currentRightSafeAreaInset = self.view.safeAreaInsets.right
+            
+        
+    }
+    
+    override func didReceiveMemoryWarning() {
+        super.didReceiveMemoryWarning()
+    }
+    
+    @objc func didDoubleTapWith(gestureRecognizer: UITapGestureRecognizer) {
+        let pointInView = gestureRecognizer.location(in: self.imageView)
+        var newZoomScale = self.scrollView.maximumZoomScale
+        
+        if self.scrollView.zoomScale >= newZoomScale || abs(self.scrollView.zoomScale - newZoomScale) <= 0.01 {
+            newZoomScale = self.scrollView.minimumZoomScale
+        }
+        
+        let width = self.scrollView.bounds.width / newZoomScale
+        let height = self.scrollView.bounds.height / newZoomScale
+        let originX = pointInView.x - (width / 2.0)
+        let originY = pointInView.y - (height / 2.0)
+        
+        let rectToZoomTo = CGRect(x: originX, y: originY, width: width, height: height)
+        self.scrollView.zoom(to: rectToZoomTo, animated: true)
+    }
+    
+    fileprivate func updateZoomScaleForSize(_ size: CGSize) {
+        
+        let widthScale = size.width / imageView.bounds.width
+        let heightScale = size.height / imageView.bounds.height
+        let minScale = min(widthScale, heightScale)
+        scrollView.minimumZoomScale = minScale
+        
+        scrollView.zoomScale = minScale
+        scrollView.maximumZoomScale = minScale * 4
+    }
+    
+    fileprivate func updateConstraintsForSize(_ size: CGSize) {
+        let yOffset = max(0, (size.height - imageView.frame.height) / 2)
+        imageViewTopConstraint.constant = yOffset
+        imageViewBottomConstraint.constant = yOffset
+        
+        let xOffset = max(0, (size.width - imageView.frame.width) / 2)
+        imageViewLeadingConstraint.constant = xOffset
+        imageViewTrailingConstraint.constant = xOffset
+
+        let contentHeight = yOffset * 2 + self.imageView.frame.height
+        view.layoutIfNeeded()
+        self.scrollView.contentSize = CGSize(width: self.scrollView.contentSize.width, height: contentHeight)
+    }
+}
+
+extension PhotoZoomViewController: UIScrollViewDelegate {
+    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
+        return imageView
+    }
+    
+    func scrollViewDidZoom(_ scrollView: UIScrollView) {
+        updateConstraintsForSize(self.view.bounds.size)
+    }
+    
+    func scrollViewDidScroll(_ scrollView: UIScrollView) {
+        self.delegate?.photoZoomViewController(self, scrollViewDidScroll: scrollView)
+    }
+}

+ 112 - 0
iOSClient/Viewer/NCViewerImage/NCViewerImage.storyboard

@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17156" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="ne8-hS-cp3">
+    <device id="retina6_1" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17126"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="System colors in document resources" minToolsVersion="11.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--Zoom-->
+        <scene sceneID="yQe-Pb-BKZ">
+            <objects>
+                <viewController automaticallyAdjustsScrollViewInsets="NO" hidesBottomBarWhenPushed="YES" id="ne8-hS-cp3" customClass="PhotoPageContainerViewController" customModule="Nextcloud" customModuleProvider="target" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="8k6-KQ-iNa"/>
+                        <viewControllerLayoutGuide type="bottom" id="baP-ZX-MR4"/>
+                    </layoutGuides>
+                    <view key="view" opaque="NO" contentMode="scaleToFill" id="wvY-tB-6ZK">
+                        <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fpt-Rz-5fT">
+                                <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
+                                <connections>
+                                    <segue destination="A4d-OP-AMT" kind="embed" id="sDx-bX-1Jt"/>
+                                </connections>
+                            </containerView>
+                        </subviews>
+                        <viewLayoutGuide key="safeArea" id="Ozy-9S-dMl"/>
+                        <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                        <constraints>
+                            <constraint firstAttribute="bottom" secondItem="fpt-Rz-5fT" secondAttribute="bottom" id="4Xy-Dl-e47"/>
+                            <constraint firstItem="fpt-Rz-5fT" firstAttribute="leading" secondItem="wvY-tB-6ZK" secondAttribute="leading" id="Kzi-Hg-Ocf"/>
+                            <constraint firstItem="fpt-Rz-5fT" firstAttribute="top" secondItem="wvY-tB-6ZK" secondAttribute="top" id="NnL-9A-ht1"/>
+                            <constraint firstAttribute="trailing" secondItem="fpt-Rz-5fT" secondAttribute="trailing" id="xh9-IP-ti7"/>
+                        </constraints>
+                    </view>
+                    <navigationItem key="navigationItem" title="Zoom" id="azh-DM-TLz"/>
+                    <nil key="simulatedBottomBarMetrics"/>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="qhs-BU-mEq" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="2911.1999999999998" y="777.66116941529242"/>
+        </scene>
+        <!--Page View Controller-->
+        <scene sceneID="qwx-FP-Pov">
+            <objects>
+                <pageViewController automaticallyAdjustsScrollViewInsets="NO" transitionStyle="scroll" pageSpacing="10" navigationOrientation="horizontal" spineLocation="none" id="A4d-OP-AMT" sceneMemberID="viewController"/>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="Q9C-FA-Vhg" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="3748" y="777.66116941529242"/>
+        </scene>
+        <!--Photo Zoom View Controller-->
+        <scene sceneID="rpT-vc-Kiv">
+            <objects>
+                <viewController storyboardIdentifier="PhotoZoomViewController" automaticallyAdjustsScrollViewInsets="NO" id="gXy-ag-ank" customClass="PhotoZoomViewController" customModule="Nextcloud" customModuleProvider="target" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="IQm-1K-tBr"/>
+                        <viewControllerLayoutGuide type="bottom" id="zwn-Sc-mqc"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="fIE-H6-KKc">
+                        <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" maximumZoomScale="4" translatesAutoresizingMaskIntoConstraints="NO" id="CdQ-LC-Trx">
+                                <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
+                                <subviews>
+                                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="1" translatesAutoresizingMaskIntoConstraints="NO" id="kPV-JM-UnM">
+                                        <rect key="frame" x="0.0" y="0.0" width="320" height="206"/>
+                                    </imageView>
+                                </subviews>
+                                <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <constraints>
+                                    <constraint firstItem="kPV-JM-UnM" firstAttribute="leading" secondItem="CdQ-LC-Trx" secondAttribute="leading" id="asL-Ft-Lmc"/>
+                                    <constraint firstAttribute="trailing" secondItem="kPV-JM-UnM" secondAttribute="trailing" id="fBN-70-6ry"/>
+                                    <constraint firstItem="kPV-JM-UnM" firstAttribute="top" secondItem="CdQ-LC-Trx" secondAttribute="top" id="tdo-XY-uqv"/>
+                                    <constraint firstAttribute="bottom" secondItem="kPV-JM-UnM" secondAttribute="bottom" id="xTI-ae-JfJ"/>
+                                </constraints>
+                            </scrollView>
+                        </subviews>
+                        <viewLayoutGuide key="safeArea" id="Yo6-7W-moG"/>
+                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+                        <constraints>
+                            <constraint firstAttribute="bottom" secondItem="CdQ-LC-Trx" secondAttribute="bottom" id="4qB-8y-OcG"/>
+                            <constraint firstAttribute="trailing" secondItem="CdQ-LC-Trx" secondAttribute="trailing" id="IwE-oE-d3Y"/>
+                            <constraint firstItem="CdQ-LC-Trx" firstAttribute="leading" secondItem="fIE-H6-KKc" secondAttribute="leading" id="g8C-2m-KkX"/>
+                            <constraint firstItem="CdQ-LC-Trx" firstAttribute="top" secondItem="fIE-H6-KKc" secondAttribute="top" id="hcQ-lB-JwU"/>
+                        </constraints>
+                    </view>
+                    <connections>
+                        <outlet property="imageView" destination="kPV-JM-UnM" id="TEv-Tc-8pu"/>
+                        <outlet property="imageViewBottomConstraint" destination="xTI-ae-JfJ" id="vdW-Ma-BV1"/>
+                        <outlet property="imageViewLeadingConstraint" destination="asL-Ft-Lmc" id="4cu-ey-QS1"/>
+                        <outlet property="imageViewTopConstraint" destination="tdo-XY-uqv" id="9Ty-3b-gkL"/>
+                        <outlet property="imageViewTrailingConstraint" destination="fBN-70-6ry" id="dZZ-2L-0VJ"/>
+                        <outlet property="scrollView" destination="CdQ-LC-Trx" id="3np-FR-s39"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="fbE-Jv-mLH" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="4548" y="778"/>
+        </scene>
+    </scenes>
+    <resources>
+        <image name="1" width="320" height="206"/>
+        <systemColor name="systemBackgroundColor">
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+    </resources>
+</document>

+ 0 - 437
iOSClient/Viewer/NCViewerImage/NCViewerImage.swift

@@ -1,437 +0,0 @@
-//
-//  NCViewerImage.swift
-//  Nextcloud
-//
-//  Created by Marino Faggiana on 18/02/20.
-//  Copyright © 2020 Marino Faggiana. All rights reserved.
-//
-//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
-//
-//  This program is free software: you can redistribute it and/or modify
-//  it under the terms of the GNU General Public License as published by
-//  the Free Software Foundation, either version 3 of the License, or
-//  (at your option) any later version.
-//
-//  This program is distributed in the hope that it will be useful,
-//  but WITHOUT ANY WARRANTY; without even the implied warranty of
-//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//  GNU General Public License for more details.
-//
-//  You should have received a copy of the GNU General Public License
-//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
-//
-
-import Foundation
-import NCCommunication
-
-class NCViewerImage: NSObject {
-
-    private weak var metadata: tableMetadata?
-    private let appDelegate = UIApplication.shared.delegate as! AppDelegate
-    
-    private var viewerImageViewController: NCViewerImageViewController?
-    private var metadatas = [tableMetadata]()
-      
-    private var favoriteFilterImage: Bool = false
-    private var mediaFilterImage: Bool = false
-    private var offlineFilterImage: Bool = false
-
-    private weak var detailViewController: NCDetailViewController?
-    
-    private var videoLayer: AVPlayerLayer?
-    private var viewerImageViewControllerLongPressInProgress = false
-
-    init(_ metadata: tableMetadata, metadatas: [tableMetadata], detailViewController: NCDetailViewController, favoriteFilterImage: Bool, mediaFilterImage: Bool, offlineFilterImage: Bool) {
-        
-        self.metadata = metadata
-        self.metadatas = metadatas
-        self.detailViewController = detailViewController
-        self.favoriteFilterImage = favoriteFilterImage
-        self.mediaFilterImage = mediaFilterImage
-        self.offlineFilterImage = offlineFilterImage
-    }
-        
-    func viewImage() {
-        guard let detailViewController = detailViewController else { return }
-        viewerImageViewController = NCViewerImageViewController(index: 0, dataSource: self, delegate: self)
-                
-        for view in detailViewController.backgroundView.subviews { view.removeFromSuperview() }
-        
-        if let metadatas = NCViewerImageCommon.shared.getMetadatasDatasource(metadata: self.metadata, metadatas: self.metadatas, favoriteDatasorce: favoriteFilterImage, mediaDatasorce: mediaFilterImage, offLineDatasource: offlineFilterImage) {
-                            
-            var index = 0
-            if let indexFound = metadatas.firstIndex(where: { $0.ocId == self.metadata?.ocId }) { index = indexFound }
-            self.metadatas = metadatas
-            
-            self.viewerImageViewController = NCViewerImageViewController(index: index, dataSource: self, delegate: self)
-            if viewerImageViewController != nil {
-                           
-                detailViewController.backgroundView.image = nil
-
-                viewerImageViewController!.view.isHidden = true
-                
-                viewerImageViewController!.enableInteractiveDismissal = true
-                
-                detailViewController.addChild(viewerImageViewController!)
-                detailViewController.backgroundView.addSubview(viewerImageViewController!.view)
-                
-                viewerImageViewController!.view.frame = CGRect(x: 0, y: 0, width: detailViewController.backgroundView.frame.width, height: detailViewController.backgroundView.frame.height)
-                viewerImageViewController!.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
-                
-                viewerImageViewController!.didMove(toParent: detailViewController)
-                
-                DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {
-                    self.viewerImageViewController!.changeInViewSize(to: detailViewController.backgroundView.frame.size)
-                    self.viewerImageViewController!.view.isHidden = false
-                }
-            }
-        }
-        
-        NotificationCenter.default.addObserver(self, selector: #selector(self.synchronizationMedia(_:)), name: NSNotification.Name(rawValue: k_notificationCenter_synchronizationMedia), object: nil)
-        NotificationCenter.default.addObserver(self, selector: #selector(self.changeDisplayMode), name: NSNotification.Name(rawValue: k_notificationCenter_splitViewChangeDisplayMode), object: nil)
-        
-        NotificationCenter.default.addObserver(self, selector: #selector(self.downloadFile(_:)), name: NSNotification.Name(rawValue: k_notificationCenter_downloadFile), object: nil)
-        NotificationCenter.default.addObserver(self, selector: #selector(self.deleteFile(_:)), name: NSNotification.Name(rawValue: k_notificationCenter_deleteFile), object: nil)
-        NotificationCenter.default.addObserver(self, selector: #selector(self.renameFile(_:)), name: NSNotification.Name(rawValue: k_notificationCenter_renameFile), object: nil)
-        NotificationCenter.default.addObserver(self, selector: #selector(self.moveFile(_:)), name: NSNotification.Name(rawValue: k_notificationCenter_moveFile), object: nil)
-    }
-    
-    //MARK: - NotificationCenter
-
-    @objc func changeDisplayMode() {
-        guard let detailViewController = detailViewController else { return }
-        NCViewerImageCommon.shared.imageChangeSizeView(viewerImageViewController: viewerImageViewController, size: detailViewController.backgroundView.frame.size, metadata: metadata)
-    }
-    
-    @objc func synchronizationMedia(_ notification: NSNotification) {
-        if let userInfo = notification.userInfo as NSDictionary? {
-            if let type = userInfo["type"] as? String {
-                
-                if self.mediaFilterImage {
-                    
-                    if let metadatas = appDelegate.activeMedia.sectionDatasource.metadatas as? [tableMetadata] {
-                        self.metadatas = metadatas
-                    }
-                    
-                    if type == "delete" {
-                        if metadatas.count > 0 {
-                            var index = viewerImageViewController!.index - 1
-                            if index < 0 { index = 0}
-                            self.metadata = metadatas[index]
-                            viewImage()
-                            
-                        } else {
-                            
-                            detailViewController?.viewUnload()
-                        }
-                    }
-                    
-                    if type == "rename" || type == "move"   {
-                        viewerImageViewController?.reloadContentViews()
-                    }
-                }
-            }
-        }
-    }
-
-    @objc func moveFile(_ notification: NSNotification) {
-        deleteFile(notification)
-    }
-    
-    @objc func deleteFile(_ notification: NSNotification) {
-        if let userInfo = notification.userInfo as NSDictionary? {
-            if let metadata = userInfo["metadata"] as? tableMetadata, let errorCode = userInfo["errorCode"] as? Int {
-                if errorCode != 0 || metadata.account != self.metadata?.account || metadata.serverUrl != self.metadata?.serverUrl { return }
-                
-                if !mediaFilterImage {
-                    if let metadatas = NCViewerImageCommon.shared.getMetadatasDatasource(metadata: self.metadata, metadatas: self.metadatas, favoriteDatasorce: favoriteFilterImage, mediaDatasorce: mediaFilterImage, offLineDatasource: offlineFilterImage) {
-                        var index = viewerImageViewController!.index - 1
-                        if index < 0 { index = 0}
-                        self.metadata = metadatas[index]
-                        viewImage()
-                    } else {
-                        detailViewController?.viewUnload()
-                    }
-                }
-            }
-        }
-    }
-    
-    @objc func renameFile(_ notification: NSNotification) {
-        if let userInfo = notification.userInfo as NSDictionary? {
-            if let metadata = userInfo["metadata"] as? tableMetadata, let errorCode = userInfo["errorCode"] as? Int {
-                if errorCode != 0 || metadata.account != self.metadata?.account || metadata.serverUrl != self.metadata?.serverUrl { return }
-                
-                if !mediaFilterImage {
-                    
-                    if NCViewerImageCommon.shared.getMetadatasDatasource(metadata: self.metadata, metadatas: self.metadatas, favoriteDatasorce: favoriteFilterImage, mediaDatasorce: mediaFilterImage, offLineDatasource: offlineFilterImage) != nil {
-                        viewImage()
-                    } else {
-                        detailViewController?.viewUnload()
-                    }
-                }
-            }
-        }
-    }
-    
-    @objc func downloadFile(_ notification: NSNotification) {
-        if let userInfo = notification.userInfo as NSDictionary? {
-            if let metadata = userInfo["metadata"] as? tableMetadata, let errorCode = userInfo["errorCode"] as? Int {
-                if metadata.account != self.metadata?.account || metadata.serverUrl != self.metadata?.serverUrl { return }
-                                
-                if  errorCode == 0  {
-                    viewerImageViewController?.reloadContentViews()
-                }
-            }
-        }
-    }
-}
-
-//MARK: - viewerImageViewController - Delegate/DataSource
-
-extension NCViewerImage: NCViewerImageViewControllerDelegate, NCViewerImageViewControllerDataSource {
-    
-    func numberOfItems(in viewerImageViewController: NCViewerImageViewController) -> Int {
-        return metadatas.count
-    }
-
-    func viewerImageViewController(_ viewerImageViewController: NCViewerImageViewController, imageAt index: Int, completion: @escaping (_ index: Int, _ image: UIImage?, _ metadata: tableMetadata, _ zoomScale: ZoomScale?, _ error: Error?) -> Void) {
-        
-        if index >= metadatas.count { return }
-        guard let detailViewController = detailViewController else { return }
-
-        let metadata = metadatas[index]
-        let isPreview = CCUtility.fileProviderStorageIconExists(metadata.ocId, fileNameView: metadata.fileNameView)
-        let isImage = CCUtility.fileProviderStorageSize(metadata.ocId, fileNameView: metadata.fileNameView) > 0
-        
-        // Refresh self metadata && title
-        if viewerImageViewController.index < metadatas.count {
-            self.metadata = metadatas[viewerImageViewController.index]
-            detailViewController.metadata = self.metadata
-            detailViewController.navigationController?.navigationBar.topItem?.title = self.metadata!.fileNameView
-            
-        }
-        
-        // Status Current
-        if index == viewerImageViewController.currentItemIndex {
-            statusViewImage(metadata: metadata, viewerImageViewController: viewerImageViewController)
-        }
-        
-        // Preview for Video
-        if metadata.typeFile == k_metadataTypeFile_video && !isPreview && isImage {
-            
-            CCGraphics.createNewImage(from: metadata.fileNameView, ocId: metadata.ocId, filterGrayScale: false, typeFile: metadata.typeFile, writeImage: true)
-        }
-        
-        // Original only for actual
-        if metadata.typeFile == k_metadataTypeFile_image && isImage && index == viewerImageViewController.index {
-                
-            if let image = NCViewerImageCommon.shared.getImage(metadata: metadata) {
-                completion(index, image, metadata, ZoomScale.default, nil)
-            } else {
-                completion(index, NCViewerImageCommon.shared.getImageOffOutline(frame: detailViewController.view.frame, type: metadata.typeFile), metadata, ZoomScale.default, nil)
-            }
-                
-        // HEIC
-        } else if metadata.contentType == "image/heic" && CCUtility.fileProviderStorageSize(metadata.ocId, fileNameView: metadata.fileNameView) == 0 && metadata.hasPreview == false {
-            
-            let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName
-            let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileName)!
-            
-            _ = NCCommunication.sharedInstance.download(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, account: metadata.account, progressHandler: { (progress) in }) { (account, etag, date, length, errorCode, errorDescription) in
-                                
-                if errorCode == 0 && account == metadata.account {
-                    
-                    _ = NCManageDatabase.sharedInstance.addLocalFile(metadata: metadata)
-                    
-                    if let image = UIImage.init(contentsOfFile: fileNameLocalPath) {
-                        
-                        CCGraphics.createNewImage(from: metadata.fileNameView, ocId: metadata.ocId, filterGrayScale: false, typeFile: metadata.typeFile, writeImage: true)
-                        
-                        completion(index, image, metadata, ZoomScale.default, nil)
-                    } else {
-                        completion(index, NCViewerImageCommon.shared.getImageOffOutline(frame: detailViewController.view.frame, type: metadata.typeFile), metadata, ZoomScale.default, nil)
-                    }
-                } else if errorCode != 0 {
-                    completion(index, NCViewerImageCommon.shared.getImageOffOutline(frame: detailViewController.view.frame, type: metadata.typeFile), metadata, ZoomScale.default, nil)
-                }
-            }
-        
-        // Preview
-        } else if isPreview {
-                
-            if let image = NCViewerImageCommon.shared.getThumbnailImage(metadata: metadata) {
-                completion(index, image, metadata, ZoomScale.default, nil)
-            } else {
-                completion(index, NCViewerImageCommon.shared.getImageOffOutline(frame: detailViewController.view.frame, type: metadata.typeFile), metadata, ZoomScale.default, nil)
-            }
-    
-        } else if metadata.hasPreview {
-                
-            let fileNamePath = CCUtility.returnFileNamePath(fromFileName: metadata.fileName, serverUrl: metadata.serverUrl, activeUrl: appDelegate.activeUrl)!
-            let fileNameLocalPath = CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, fileNameView: metadata.fileNameView)!
-                    
-            NCCommunication.sharedInstance.downloadPreview(serverUrl: appDelegate.activeUrl, fileNamePath: fileNamePath, fileNameLocalPath: fileNameLocalPath, width: CGFloat(k_sizePreview), height: CGFloat(k_sizePreview), account: metadata.account) { (account, data, errorCode, errorMessage) in
-                if errorCode == 0 && data != nil {
-                    completion(index, UIImage.init(data: data!), metadata, ZoomScale.default, nil)
-                } else {
-                    completion(index, NCViewerImageCommon.shared.getImageOffOutline(frame: detailViewController.view.frame, type: metadata.typeFile), metadata, ZoomScale.default, nil)
-                }
-            }
-        } else {
-            completion(index, NCViewerImageCommon.shared.getImageOffOutline(frame: detailViewController.view.frame, type: metadata.typeFile), metadata, ZoomScale.default, nil)
-        }
-    }
-    
-    func viewerImageViewController(_ viewerImageViewController: NCViewerImageViewController, didChangeFocusTo index: Int, view: NCViewerImageContentView, metadata: tableMetadata) {
-                
-        if metadata.typeFile == k_metadataTypeFile_image {
-            DispatchQueue.global().async {
-                if let image = NCViewerImageCommon.shared.getImage(metadata: metadata) {
-                    DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(400)) {
-                        view.image = image
-                    }
-                }
-            }
-        }
-        
-        statusViewImage(metadata: metadata, viewerImageViewController: viewerImageViewController)
-    }
-    
-    func viewerImageViewControllerTap(_ viewerImageViewController: NCViewerImageViewController, metadata: tableMetadata) {
-        
-        guard let detailViewController = detailViewController else { return }
-        guard let navigationController = detailViewController.navigationController else { return }
-        
-        if metadata.typeFile == k_metadataTypeFile_image {
-        
-            if navigationController.isNavigationBarHidden {
-                detailViewController.navigateControllerBarHidden(false)
-                viewerImageViewController.statusView.isHidden = false
-            } else {
-                detailViewController.navigateControllerBarHidden(true)
-                viewerImageViewController.statusView.isHidden = true
-            }
-            
-            NCViewerImageCommon.shared.imageChangeSizeView(viewerImageViewController: viewerImageViewController, size: detailViewController.backgroundView.frame.size, metadata: metadata)
-            
-        } else {
-            
-            if let viewerImageVideo = UIStoryboard(name: "NCViewerImageVideo", bundle: nil).instantiateInitialViewController() as? NCViewerImageVideo {
-                viewerImageVideo.metadata = metadata
-                detailViewController.present(viewerImageVideo, animated: false) { }
-            }
-        }
-        
-        statusViewImage(metadata: metadata, viewerImageViewController: viewerImageViewController)
-    }
-    
-    func viewerImageViewControllerLongPressBegan(_ viewerImageViewController: NCViewerImageViewController, metadata: tableMetadata) {
-        
-        viewerImageViewControllerLongPressInProgress = true
-        
-        let fileName = (metadata.fileNameView as NSString).deletingPathExtension + ".mov"
-        if let metadata = NCManageDatabase.sharedInstance.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameView LIKE[c] %@", metadata.account, metadata.serverUrl, fileName)) {
-            
-            if CCUtility.fileProviderStorageSize(metadata.ocId, fileNameView: metadata.fileNameView) > 0 {
-                
-                viewMOV(viewerImageViewController: viewerImageViewController, metadata: metadata)
-                
-            } else {
-                
-                let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName
-                let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileName)!
-                                
-                _ = NCCommunication.sharedInstance.download(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, account: metadata.account, progressHandler: { (progress) in
-                                                        
-                }) { (account, etag, date, length, errorCode, errorDescription) in
-                                        
-                    if errorCode == 0 && account == metadata.account {
-                        
-                        _ = NCManageDatabase.sharedInstance.addLocalFile(metadata: metadata)
-                        self.viewMOV(viewerImageViewController: viewerImageViewController, metadata: metadata)
-                    }
-                }
-            }
-        }
-    }
-    
-    func viewerImageViewControllerLongPressEnded(_ viewerImageViewController: NCViewerImageViewController, metadata: tableMetadata) {
-        
-        viewerImageViewControllerLongPressInProgress = false
-        
-        appDelegate.player?.pause()
-        videoLayer?.removeFromSuperlayer()
-    }
-    
-    func viewerImageViewControllerDismiss() {
-        detailViewController?.viewUnload()
-    }
-    
-    @objc func downloadImage() {
-        
-        guard let metadata = self.metadata else {return }
-        
-        metadata.session = k_download_session
-        metadata.sessionError = ""
-        metadata.sessionSelector = ""
-        metadata.status = Int(k_metadataStatusWaitDownload)
-        
-        self.metadata = NCManageDatabase.sharedInstance.addMetadata(metadata)
-        
-        if let index = metadatas.firstIndex(where: { $0.ocId == metadata.ocId }) {
-            metadatas[index] = self.metadata!
-        }
-        
-        appDelegate.startLoadAutoDownloadUpload()
-    }
-    
-    func saveLivePhoto(metadata: tableMetadata, metadataMov: tableMetadata) {
-        
-        let fileNameImage = URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!)
-        let fileNameMov = URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadataMov.ocId, fileNameView: metadataMov.fileNameView)!)
-        
-        NCLivePhoto.generate(from: fileNameImage, videoURL: fileNameMov, progress: { progress in
-            self.detailViewController?.progress(Float(progress))
-        }, completion: { livePhoto, resources in
-            self.detailViewController?.progress(0)
-            if resources != nil {
-                NCLivePhoto.saveToLibrary(resources!) { (result) in
-                    if !result {
-                        NCContentPresenter.shared.messageNotification("_error_", description: "_livephoto_save_error_", delay: TimeInterval(k_dismissAfterSecond), type: NCContentPresenter.messageType.error, errorCode: Int(k_CCErrorInternalError))
-                    }
-                }
-            } else {
-                NCContentPresenter.shared.messageNotification("_error_", description: "_livephoto_save_error_", delay: TimeInterval(k_dismissAfterSecond), type: NCContentPresenter.messageType.error, errorCode: Int(k_CCErrorInternalError))
-            }
-        })
-    }
-    
-    func statusViewImage(metadata: tableMetadata, viewerImageViewController: NCViewerImageViewController) {
-        
-        var colorStatus: UIColor = UIColor.white.withAlphaComponent(0.8)
-        if detailViewController?.view.backgroundColor?.isLight() ?? true { colorStatus = UIColor.black.withAlphaComponent(0.8) }
-        
-        if NCUtility.sharedInstance.hasMOV(metadata: metadata) != nil {
-            viewerImageViewController.statusView.image = CCGraphics.changeThemingColorImage(UIImage.init(named: "livePhoto"), width: 100, height: 100, color: colorStatus)
-        } else if metadata.typeFile == k_metadataTypeFile_video || metadata.typeFile == k_metadataTypeFile_audio {
-            viewerImageViewController.statusView.image = CCGraphics.changeThemingColorImage(UIImage.init(named: "play"), width: 100, height: 100, color: colorStatus)
-        } else {
-            viewerImageViewController.statusView.image = nil
-        }
-    }
-    
-    func viewMOV(viewerImageViewController: NCViewerImageViewController, metadata: tableMetadata) {
-        
-        if !viewerImageViewControllerLongPressInProgress { return }
-        
-        appDelegate.player = AVPlayer(url: URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!))
-        videoLayer = AVPlayerLayer(player: appDelegate.player)
-        if  videoLayer != nil {
-            videoLayer!.frame = viewerImageViewController.view.frame
-            videoLayer!.videoGravity = AVLayerVideoGravity.resizeAspect
-            viewerImageViewController.view.layer.addSublayer(videoLayer!)
-            appDelegate.player?.play()
-        }
-    }
-}

+ 0 - 158
iOSClient/Viewer/NCViewerImage/NCViewerImageCommon.swift

@@ -1,158 +0,0 @@
-//
-//  NCViewerImageCommon.swift
-//  Nextcloud
-//
-//  Created by Marino Faggiana on 04/03/2020.
-//  Copyright © 2020 Marino Faggiana. All rights reserved.
-//
-//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
-//
-//  This program is free software: you can redistribute it and/or modify
-//  it under the terms of the GNU General Public License as published by
-//  the Free Software Foundation, either version 3 of the License, or
-//  (at your option) any later version.
-//
-//  This program is distributed in the hope that it will be useful,
-//  but WITHOUT ANY WARRANTY; without even the implied warranty of
-//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//  GNU General Public License for more details.
-//
-//  You should have received a copy of the GNU General Public License
-//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
-//
-
-import Foundation
-import SVGKit
-
-class NCViewerImageCommon: NSObject {
-    @objc static let shared: NCViewerImageCommon = {
-        let instance = NCViewerImageCommon()
-        return instance
-    }()
-    
-    private let appDelegate = UIApplication.shared.delegate as! AppDelegate
-
-    static var offOutlineAudio: UIImage?
-    static var offOutlineVideo: UIImage?
-    static var offOutlineImage: UIImage?
-
-    override init() {
-        NCViewerImageCommon.offOutlineAudio = CCGraphics.changeThemingColorImage(UIImage.init(named: "offOutlineAudio"), width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.width, color: NCBrandColor.sharedInstance.brandElement)
-        NCViewerImageCommon.offOutlineVideo = CCGraphics.changeThemingColorImage(UIImage.init(named: "offOutlineVideo"), width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.width, color: NCBrandColor.sharedInstance.brandElement)
-        NCViewerImageCommon.offOutlineImage = CCGraphics.changeThemingColorImage(UIImage.init(named: "offOutlineImage"), width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.width, color: NCBrandColor.sharedInstance.brandElement)
-    }
-    
-    func getMetadatasDatasource(metadata: tableMetadata?, mediaDatasorce: Bool, layoutKey: String, completion: @escaping (_ metadatas: [tableMetadata]?) -> Void) {
-        guard let metadata = metadata else {
-            completion(nil)
-            return
-        }
-        if mediaDatasorce {
-            appDelegate.activeMedia.reloadDataSourceWithCompletion { (metadatas) in
-                completion(metadatas)
-            }
-        } else {
-            let metadatas = NCManageDatabase.sharedInstance.getMetadatasViewer(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND (typeFile == %@ || typeFile == %@ || typeFile == %@)", metadata.account, metadata.serverUrl, k_metadataTypeFile_image, k_metadataTypeFile_video, k_metadataTypeFile_audio), sorted: NCUtility.shared.getSortedForView(key: layoutKey, serverUrl: metadata.serverUrl), ascending: NCUtility.shared.getAscendingForView(key: layoutKey, serverUrl: metadata.serverUrl))
-            completion(metadatas)
-        }
-    }
-    
-    func getThumbnailImage(metadata: tableMetadata) -> UIImage? {
-        
-        if CCUtility.fileProviderStoragePreviewIconExists(metadata.ocId, etag: metadata.etag) {
-            let imagePath = CCUtility.getDirectoryProviderStoragePreviewOcId(metadata.ocId, etag: metadata.etag)!
-            return UIImage.init(contentsOfFile: imagePath)
-        }
-        
-        return nil
-    }
-    
-    func getImage(metadata: tableMetadata) -> UIImage? {
-        
-        let ext = CCUtility.getExtension(metadata.fileNameView)
-        var image: UIImage?
-        
-        if CCUtility.fileProviderStorageSize(metadata.ocId, fileNameView: metadata.fileNameView) > 0 && metadata.typeFile == k_metadataTypeFile_image {
-           
-            let previewPath = CCUtility.getDirectoryProviderStoragePreviewOcId(metadata.ocId, etag: metadata.etag)!
-            let imagePath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!
-            
-            if ext == "GIF" {
-                if !FileManager().fileExists(atPath: previewPath) {
-                    CCGraphics.createNewImage(from: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, typeFile: metadata.typeFile)
-                }
-                image = UIImage.animatedImage(withAnimatedGIFURL: URL(fileURLWithPath: imagePath))
-            } else if ext == "SVG" {
-                if let svgImage = SVGKImage(contentsOfFile: imagePath) {
-                    let scale = svgImage.size.height / svgImage.size.width
-                    svgImage.size = CGSize(width: CGFloat(k_sizePreview), height: (CGFloat(k_sizePreview) * scale))
-                    if let image = svgImage.uiImage {
-                        if !FileManager().fileExists(atPath: previewPath) {
-                            do {
-                                try image.pngData()?.write(to: URL(fileURLWithPath: previewPath), options: .atomic)
-                            } catch { }
-                        }
-                        return image
-                    } else {
-                        return nil
-                    }
-                } else {
-                    return nil
-                }
-            } else {
-                if !FileManager().fileExists(atPath: previewPath) {
-                    CCGraphics.createNewImage(from: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, typeFile: metadata.typeFile)
-                }
-                image = UIImage.init(contentsOfFile: imagePath)
-            }
-            
-        } else {
-            
-            // AUTOMATIC DOWNLOAD FOR GIF
-            
-            if (ext == "GIF" || ext == "SVG")  && metadata.session == "" {
-                NotificationCenter.default.postOnMainThread(name: k_notificationCenter_menuDownloadImage, userInfo: ["metadata": metadata])
-            }
-        }
-        
-        return image
-    }
-    
-    func imageChangeSizeView(viewerImageViewController: NCViewerImageViewController?, size: CGSize, metadata: tableMetadata?) {
-        guard let viewerImageViewController = viewerImageViewController else { return }
-        
-        var image: UIImage?
-        var contentViewSaved : NCViewerImageContentView?
-        for contentView in viewerImageViewController.contentViews {
-            if contentView.position == 0 && contentView.isLoading == false {
-                image = contentView.image
-                contentViewSaved = contentView
-                if metadata != nil , let thumbnailImage = self.getThumbnailImage(metadata: metadata!) {
-                    contentView.image = thumbnailImage
-                } else {
-                    contentView.image = nil
-                }
-            }
-        }
-        
-        DispatchQueue.main.async {
-            viewerImageViewController.changeInViewSize(to: size)
-            if image != nil {
-                contentViewSaved?.image = image
-            }
-        }
-    }
-
-    func getImageOffOutline(frame: CGRect, type: String) -> UIImage {
-        
-        if type == k_metadataTypeFile_video {
-            return NCViewerImageCommon.offOutlineVideo!
-        }
-        
-        if type == k_metadataTypeFile_audio {
-            return NCViewerImageCommon.offOutlineAudio!
-        }
-        
-        return NCViewerImageCommon.offOutlineImage!
-    }
-}

+ 0 - 234
iOSClient/Viewer/NCViewerImage/NCViewerImageContentTransformers.swift

@@ -1,234 +0,0 @@
-//
-//  ContentTransformers.swift
-//  Nextcloud
-//
-//  Created by Suraj Thomas K on 7/17/18 Copyright © 2018 Al Tayer Group LLC..
-//  Modify for Nextcloud by Marino Faggiana on 04/03/2020.
-//
-//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
-//
-//  This program is free software: you can redistribute it and/or modify
-//  it under the terms of the GNU General Public License as published by
-//  the Free Software Foundation, either version 3 of the License, or
-//  (at your option) any later version.
-//
-//  This program is distributed in the hope that it will be useful,
-//  but WITHOUT ANY WARRANTY; without even the implied warranty of
-//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//  GNU General Public License for more details.
-//
-//  You should have received a copy of the GNU General Public License
-//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
-//
-
-/**
- Content transformer used for transition between media item views.
-
- - parameter contentView: The content view on which transform corresponding to the position has to be applied.
- - parameter position: Current position for the passed content view.
-
- - note:
-    The trasnform to be applied on the contentView has to be dependent on the position passed.
-    The position value can be -ve, 0.0 or positive.
-
-    Try to visualize content views at -1.0[previous]=>0.0[current]=>1.0[next].
-
-    1. When position is -1.0, the content view should be at the place meant for previous view.
-
-    2. When the position is 0.0, the transform applied on the content view should make it visible full screen at origin.
-
-    3. When position is 1.0, the content view should be at the place meant for next view.
-
-    Be mindful of the drawing order, when designing new transitions.
- */
-public typealias NCViewerImageContentTransformer = (_ contentView: UIView, _ position: CGFloat) -> Void
-
-// MARK: - Default Transitions
-
-/// An enumeration to hold default content transformers
-public enum NCViewerImageDefaultContentTransformers {
-
-    /**
-     Horizontal move-in-out content transformer.
-
-     - Requires:
-         * GestureDirection: Horizontal
-    */
-    public static let horizontalMoveInOut: NCViewerImageContentTransformer = { contentView, position in
-
-        let widthIncludingGap = contentView.bounds.size.width + NCViewerImageContentView.interItemSpacing
-        contentView.transform = CGAffineTransform(translationX: widthIncludingGap * position, y: 0.0)
-    }
-
-    /**
-     Vertical move-in-out content transformer.
-
-     - Requires:
-        * GestureDirection: Vertical
-     */
-    public static let verticalMoveInOut: NCViewerImageContentTransformer = { contentView, position in
-
-        let heightIncludingGap = contentView.bounds.size.height + NCViewerImageContentView.interItemSpacing
-        contentView.transform = CGAffineTransform(translationX: 0.0, y: heightIncludingGap * position)
-    }
-
-    /**
-     Horizontal slide-out content transformer.
-
-     - Requires:
-        * GestureDirection: Horizontal
-        * DrawOrder: PreviousToNext
-     */
-    public static let horizontalSlideOut: NCViewerImageContentTransformer = { contentView, position in
-
-        var scale: CGFloat = 1.0
-        if position < -0.5 {
-            scale = 0.9
-        } else if -0.5...0.0 ~= Double(position) {
-            scale = 1.0 + (position * 0.2)
-        }
-        var transform = CGAffineTransform(scaleX: scale, y: scale)
-
-        let widthIncludingGap = contentView.bounds.size.width + NCViewerImageContentView.interItemSpacing
-        let x = position >= 0.0 ? widthIncludingGap * position : 0.0
-        transform = transform.translatedBy(x: x, y: 0.0)
-
-        contentView.transform = transform
-
-        let margin: CGFloat = 0.0000001
-        contentView.isHidden = ((1.0-margin)...(1.0+margin) ~= abs(position))
-    }
-
-    /**
-     Vertical slide-out content transformer.
-
-     - Requires:
-         * GestureDirection: Vertical
-         * DrawOrder: PreviousToNext
-     */
-    public static let verticalSlideOut: NCViewerImageContentTransformer = { contentView, position in
-
-        var scale: CGFloat = 1.0
-        if position < -0.5 {
-            scale = 0.9
-        } else if -0.5...0.0 ~= Double(position) {
-            scale = 1.0 + (position * 0.2)
-        }
-        var transform = CGAffineTransform(scaleX: scale, y: scale)
-
-        let heightIncludingGap = contentView.bounds.size.height + NCViewerImageContentView.interItemSpacing
-        let y = position >= 0.0 ? heightIncludingGap * position : 0.0
-        transform = transform.translatedBy(x: 0.0, y: y)
-
-        contentView.transform = transform
-
-        let margin: CGFloat = 0.0000001
-        contentView.isHidden = ((1.0-margin)...(1.0+margin) ~= abs(position))
-    }
-
-    /**
-     Horizontal slide-in content transformer.
-
-     - Requires:
-         * GestureDirection: Horizontal
-         * DrawOrder: NextToPrevious
-     */
-    public static let horizontalSlideIn: NCViewerImageContentTransformer = { contentView, position in
-
-        var scale: CGFloat = 1.0
-        if position > 0.5 {
-            scale = 0.9
-        } else if 0.0...0.5 ~= Double(position) {
-            scale = 1.0 - (position * 0.2)
-        }
-        var transform = CGAffineTransform(scaleX: scale, y: scale)
-
-        let widthIncludingGap = contentView.bounds.size.width + NCViewerImageContentView.interItemSpacing
-        let x = position > 0.0 ? 0.0 : widthIncludingGap * position
-        transform = transform.translatedBy(x: x, y: 0.0)
-
-        contentView.transform = transform
-
-        let margin: CGFloat = 0.0000001
-        contentView.isHidden = ((1.0-margin)...(1.0+margin) ~= abs(position))
-    }
-
-    /**
-     Vertical slide-in content transformer.
-
-     - Requires:
-         * GestureDirection: Vertical
-         * DrawOrder: NextToPrevious
-     */
-    public static let verticalSlideIn: NCViewerImageContentTransformer = { contentView, position in
-
-        var scale: CGFloat = 1.0
-        if position > 0.5 {
-            scale = 0.9
-        } else if 0.0...0.5 ~= Double(position) {
-            scale = 1.0 - (position * 0.2)
-        }
-        var transform = CGAffineTransform(scaleX: scale, y: scale)
-
-        let heightIncludingGap = contentView.bounds.size.height + NCViewerImageContentView.interItemSpacing
-        let y = position > 0.0 ? 0.0 : heightIncludingGap * position
-        transform = transform.translatedBy(x: 0.0, y: y)
-
-        contentView.transform = transform
-
-        let margin: CGFloat = 0.0000001
-        contentView.isHidden = ((1.0-margin)...(1.0+margin) ~= abs(position))
-    }
-
-    /**
-     Horizontal zoom-in-out content transformer.
-
-     - Requires:
-     * GestureDirection: Horizontal
-     */
-    public static let horizontalZoomInOut: NCViewerImageContentTransformer = { contentView, position in
-
-        let minScale: CGFloat = 0.5
-        // Scale factor is used to reduce the scale animation speed.
-        let scaleFactor: CGFloat = 0.5
-        var scale: CGFloat = CGFloat.maximum(minScale, 1.0 - abs(position * scaleFactor))
-
-        // Actual gap will be scaleFactor * 0.5 times of contentView.bounds.size.width.
-        let actualGap = contentView.bounds.size.width * scaleFactor * 0.5
-        let gapCorrector = NCViewerImageContentView.interItemSpacing - actualGap
-
-        let widthIncludingGap = contentView.bounds.size.width + gapCorrector
-        let translation = (widthIncludingGap * position)/scale
-
-        var transform = CGAffineTransform(scaleX: scale, y: scale)
-        transform = transform.translatedBy(x: translation, y: 0.0)
-
-        contentView.transform = transform
-    }
-
-    /**
-     Vertical zoom-in-out content transformer.
-
-     - Requires:
-     * GestureDirection: Vertical
-     */
-    public static let verticalZoomInOut: NCViewerImageContentTransformer = { contentView, position in
-
-        let minScale: CGFloat = 0.5
-        // Scale factor is used to reduce the scale animation speed.
-        let scaleFactor: CGFloat = 0.5
-        let scale: CGFloat = CGFloat.maximum(minScale, 1.0 - abs(position * scaleFactor))
-
-        // Actual gap will be scaleFactor * 0.5 times of contentView.bounds.size.height.
-        let actualGap = contentView.bounds.size.height * scaleFactor * 0.5
-        let gapCorrector = NCViewerImageContentView.interItemSpacing - actualGap
-
-        let heightIncludingGap = contentView.bounds.size.height + gapCorrector
-        let translation = (heightIncludingGap * position)/scale
-
-        var transform = CGAffineTransform(scaleX: scale, y: scale)
-        transform = transform.translatedBy(x: 0.0, y: translation)
-
-        contentView.transform = transform
-    }
-}

+ 0 - 320
iOSClient/Viewer/NCViewerImage/NCViewerImageContentView.swift

@@ -1,320 +0,0 @@
-//
-//  NCViewerImageContentView.swift
-//  Nextcloud
-//
-//  Created by Suraj Thomas K on 7/10/18 Copyright © 2018 Al Tayer Group LLC..
-//  Modify for Nextcloud by Marino Faggiana on 04/03/2020.
-//
-//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
-//
-//  This program is free software: you can redistribute it and/or modify
-//  it under the terms of the GNU General Public License as published by
-//  the Free Software Foundation, either version 3 of the License, or
-//  (at your option) any later version.
-//
-//  This program is distributed in the hope that it will be useful,
-//  but WITHOUT ANY WARRANTY; without even the implied warranty of
-//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//  GNU General Public License for more details.
-//
-//  You should have received a copy of the GNU General Public License
-//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
-//
-
-public struct ZoomScale {
-
-    public var minimumZoomScale: CGFloat
-    public var maximumZoomScale: CGFloat
-
-    public static let `default` = ZoomScale(
-        minimum: 1.0,
-        maximum: 5.0
-    )
-
-    public static let identity = ZoomScale(
-        minimum: 1.0,
-        maximum: 1.0
-    )
-
-    public init(minimum: CGFloat, maximum: CGFloat) {
-        minimumZoomScale = minimum
-        maximumZoomScale = maximum
-    }
-}
-
-public class NCViewerImageContentView: UIScrollView {
-
-    // MARK: - Exposed variables
-    
-    var metadata = tableMetadata()
-    internal static var interItemSpacing: CGFloat = 0.0
-    internal var index: Int {
-        didSet {
-            resetZoom()
-        }
-    }
-    internal static var contentTransformer: NCViewerImageContentTransformer = NCViewerImageDefaultContentTransformers.horizontalMoveInOut
-
-    internal var position: CGFloat {
-        didSet {
-            updateTransform()
-        }
-    }
-    internal var image: UIImage? {
-        didSet {
-            updateImageView()
-        }
-    }
-    internal var isLoading: Bool = false {
-        didSet {
-            indicatorContainer.isHidden = !isLoading
-            if isLoading {
-                indicator.startAnimating()
-            } else {
-                indicator.stopAnimating()
-            }
-        }
-    }
-    internal var zoomLevels: ZoomScale? {
-        didSet {
-            zoomScale = ZoomScale.default.minimumZoomScale
-            minimumZoomScale = zoomLevels?.minimumZoomScale ?? ZoomScale.default.minimumZoomScale
-            maximumZoomScale = zoomLevels?.maximumZoomScale ?? ZoomScale.default.maximumZoomScale
-        }
-    }
-    
-    // MARK: - Private enumerations
-
-    private enum Constants {
-
-        static let indicatorViewSize: CGFloat = 60.0
-    }
-
-    // MARK: - Private variables
-
-    private lazy var imageView: UIImageView = {
-        let imageView = UIImageView()
-        imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
-        imageView.contentMode = .scaleAspectFit
-        imageView.clipsToBounds = true
-        return imageView
-    }()
-
-    private lazy var indicator: UIActivityIndicatorView = {
-        let indicatorView = UIActivityIndicatorView()
-        indicatorView.style = .whiteLarge
-        indicatorView.color = .darkGray
-        indicatorView.hidesWhenStopped = true
-        return indicatorView
-    }()
-
-    private lazy var indicatorContainer: UIView = {
-        let container = UIView()
-        container.backgroundColor = .clear
-        container.layer.cornerRadius = Constants.indicatorViewSize * 0.5
-        container.layer.masksToBounds = true
-        return container
-    }()
-
-    private lazy var doubleTapGestureRecognizer: UITapGestureRecognizer = { [unowned self] in
-        let gesture = UITapGestureRecognizer(target: self, action: #selector(didDoubleTap(_:)))
-        gesture.numberOfTapsRequired = 2
-        gesture.numberOfTouchesRequired = 1
-        return gesture
-    }()
-
-    init(index itemIndex: Int, position: CGFloat, frame: CGRect) {
-
-        self.index = itemIndex
-        self.position = position
-
-        super.init(frame: frame)
-
-        initializeViewComponents()
-    }
-
-    required init?(coder aDecoder: NSCoder) {
-
-        fatalError("Do nto use `init?(coder:)`")
-    }
-}
-
-// MARK: - View Composition and Events
-
-extension NCViewerImageContentView {
-
-    private func initializeViewComponents() {
-
-        addSubview(imageView)
-        imageView.frame = frame
-
-        setupIndicatorView()
-
-        configureScrollView()
-
-        addGestureRecognizer(doubleTapGestureRecognizer)
-
-        updateTransform()
-    }
-
-    private func configureScrollView() {
-
-        isMultipleTouchEnabled = true
-        showsHorizontalScrollIndicator = false
-        showsVerticalScrollIndicator = false
-        contentSize = imageView.bounds.size
-        canCancelContentTouches = false
-        zoomLevels = ZoomScale.default
-        delegate = self
-        bouncesZoom = false
-    }
-
-    private func resetZoom() {
-
-        setZoomScale(1.0, animated: false)
-        imageView.transform = CGAffineTransform.identity
-        contentSize = imageView.frame.size
-        contentOffset = .zero
-    }
-
-    private func setupIndicatorView() {
-
-        addSubview(indicatorContainer)
-        indicatorContainer.translatesAutoresizingMaskIntoConstraints = false
-        NSLayoutConstraint.activate([
-            indicatorContainer.widthAnchor.constraint(equalToConstant: Constants.indicatorViewSize),
-            indicatorContainer.heightAnchor.constraint(equalToConstant: Constants.indicatorViewSize),
-            indicatorContainer.centerXAnchor.constraint(equalTo: centerXAnchor),
-            indicatorContainer.centerYAnchor.constraint(equalTo: centerYAnchor)
-        ])
-
-        indicatorContainer.addSubview(indicator)
-        indicator.translatesAutoresizingMaskIntoConstraints = false
-        NSLayoutConstraint.activate([
-            indicator.leadingAnchor.constraint(equalTo: indicatorContainer.leadingAnchor),
-            indicator.trailingAnchor.constraint(equalTo: indicatorContainer.trailingAnchor),
-            indicator.topAnchor.constraint(equalTo: indicatorContainer.topAnchor),
-            indicator.bottomAnchor.constraint(equalTo: indicatorContainer.bottomAnchor)
-        ])
-
-        indicatorContainer.setNeedsLayout()
-        indicatorContainer.layoutIfNeeded()
-
-        indicatorContainer.isHidden = true
-    }
-
-    internal func updateTransform() {
-
-        NCViewerImageContentView.contentTransformer(self, position)
-    }
-
-    internal func handleChangeInViewSize(to size: CGSize) {
-
-        let oldScale = zoomScale
-        zoomScale = 1.0
-        imageView.frame = CGRect(origin: .zero, size: size)
-
-        updateImageView()
-        updateTransform()
-        setZoomScale(oldScale, animated: false)
-
-        contentSize = imageView.frame.size
-    }
-    
-    func zoomScaleOne() {
-        if zoomScale == 1 { return }
-        
-        let width = bounds.size.width
-        let height = bounds.size.height
-
-        let zoomRect = CGRect(
-            x: bounds.size.width/2 - width * 0.5,
-            y: bounds.size.height/2 - height * 0.5,
-            width: width,
-            height: height
-        )
-
-        zoom(to: zoomRect, animated: true)
-    }
-
-    @objc private func didDoubleTap(_ recognizer: UITapGestureRecognizer) {
-
-        let locationInImage = recognizer.location(in: imageView)
-
-        let isImageCoveringScreen = imageView.frame.size.width > bounds.size.width &&
-            imageView.frame.size.height > bounds.size.height
-        let zoomTo = (isImageCoveringScreen || zoomScale == maximumZoomScale) ? minimumZoomScale : maximumZoomScale
-
-        guard zoomTo != zoomScale else {
-            return
-        }
-
-        let width = bounds.size.width / zoomTo
-        let height = bounds.size.height / zoomTo
-
-        let zoomRect = CGRect(
-            x: locationInImage.x - width * 0.5,
-            y: locationInImage.y - height * 0.5,
-            width: width,
-            height: height
-        )
-
-        zoom(to: zoomRect, animated: true)
-    }
-}
-
-// MARK: - UIScrollViewDelegate
-
-extension NCViewerImageContentView: UIScrollViewDelegate {
-
-    public func viewForZooming(in scrollView: UIScrollView) -> UIView? {
-
-        let shouldAllowZoom = (image != nil && position == 0.0)
-        return shouldAllowZoom ? imageView : nil
-    }
-
-    public func scrollViewDidZoom(_ scrollView: UIScrollView) {
-
-        centerImageView()
-    }
-
-    private func centerImageView() {
-
-        var imageViewFrame = imageView.frame
-
-        if imageViewFrame.size.width < bounds.size.width {
-            imageViewFrame.origin.x = (bounds.size.width - imageViewFrame.size.width) / 2.0
-        } else {
-            imageViewFrame.origin.x = 0.0
-        }
-
-        if imageViewFrame.size.height < bounds.size.height {
-            imageViewFrame.origin.y = (bounds.size.height - imageViewFrame.size.height) / 2.0
-        } else {
-            imageViewFrame.origin.y = 0.0
-        }
-
-        imageView.frame = imageViewFrame
-    }
-
-    private func updateImageView() {
-
-        imageView.image = image
-
-        if let contentImage = image {
-
-            let imageViewSize = bounds.size
-            let imageSize = contentImage.size
-            var targetImageSize = imageViewSize
-
-            if imageSize.width / imageSize.height > imageViewSize.width / imageViewSize.height {
-                targetImageSize.height = imageViewSize.width / imageSize.width * imageSize.height
-            } else {
-                targetImageSize.width = imageViewSize.height / imageSize.height * imageSize.width
-            }
-
-            imageView.frame = CGRect(origin: .zero, size: targetImageSize)
-        }
-        centerImageView()
-    }
-}

+ 0 - 261
iOSClient/Viewer/NCViewerImage/NCViewerImageDismissAnimationController.swift

@@ -1,261 +0,0 @@
-//
-//  NCViewerImageDismissAnimationController.swift
-//  Nextcloud
-//
-//  Created by Suraj Thomas K on 7/19/18 Copyright © 2018 Al Tayer Group LLC..
-//  Modify for Nextcloud by Marino Faggiana on 04/03/2020.
-//
-//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
-//
-//  This program is free software: you can redistribute it and/or modify
-//  it under the terms of the GNU General Public License as published by
-//  the Free Software Foundation, either version 3 of the License, or
-//  (at your option) any later version.
-//
-//  This program is distributed in the hope that it will be useful,
-//  but WITHOUT ANY WARRANTY; without even the implied warranty of
-//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//  GNU General Public License for more details.
-//
-//  You should have received a copy of the GNU General Public License
-//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
-//
-
-internal class NCViewerImageDismissAnimationController: NSObject {
-
-    private enum Constants {
-
-        static let minimumVelocity: CGFloat = 15.0
-        static let minimumTranslation: CGFloat = 0.25
-        static let transitionDuration = 0.3
-        static let updateFrameRate: CGFloat = 2400.0
-        static let transitionSpeedFactor: CGFloat = 0.15
-        static let minimumZoomDuringInteraction: CGFloat = 0.9
-    }
-
-    internal var image: UIImage?
-    internal let gestureDirection: NCViewerImageViewController.GestureDirection
-    internal weak var viewController: NCViewerImageViewController?
-    internal var interactionInProgress = false
-
-    private lazy var imageView = UIImageView()
-
-    private var timer: Timer?
-    private var distanceToMove: CGPoint = .zero
-    private var relativePosition: CGPoint = .zero
-    private var progressValue: CGFloat {
-        return (gestureDirection == .horizontal) ? relativePosition.y : relativePosition.x
-    }
-    private var shouldZoomOutOnInteraction = false
-
-    init(
-        image: UIImage? = nil,
-        gestureDirection: NCViewerImageViewController.GestureDirection,
-        viewController: NCViewerImageViewController
-        ) {
-
-        self.image = image
-        self.gestureDirection = gestureDirection
-        self.viewController = viewController
-    }
-
-    internal func handleInteractiveTransition(_ recognizer: UIPanGestureRecognizer) {
-
-        let translation = recognizer.translation(in: recognizer.view)
-
-        let progress = CGPoint(
-            x: translation.x / UIScreen.main.bounds.size.width,
-            y: translation.y / UIScreen.main.bounds.size.height
-        )
-
-        switch recognizer.state {
-        case .began:
-            beginTransition()
-            fallthrough
-        case .changed:
-            relativePosition = progress
-            updateTransition()
-        case .ended, .cancelled, .failed:
-            var toMove: CGFloat = 0.0
-
-            if abs(progressValue) > Constants.minimumTranslation {
-                if let viewController = viewController,
-                    let targetFrame = viewController.dataSource?.targetFrameForDismissal(viewController) {
-
-                    animateToTargetFrame(targetFrame)
-                    return
-
-                } else {
-                    toMove = (progressValue / abs(progressValue))
-                }
-            } else {
-                toMove = -progressValue
-            }
-
-            if gestureDirection == .horizontal {
-                distanceToMove.x = -relativePosition.x
-                distanceToMove.y = toMove
-            } else {
-                distanceToMove.x = toMove
-                distanceToMove.y = -relativePosition.y
-            }
-
-            if timer == nil {
-                timer = Timer.scheduledTimer(
-                    timeInterval: 1.0/Double(Constants.updateFrameRate),
-                    target: self,
-                    selector: #selector(update(_:)),
-                    userInfo: nil,
-                    repeats: true
-                )
-            }
-        default:
-            break
-        }
-    }
-
-    internal func animateToTargetFrame(_ target: CGRect) {
-
-        let frame = imageViewFrame(for: imageView.bounds.size, in: target, mode: .scaleAspectFill)
-        UIView.animate(withDuration: Constants.transitionDuration, animations: {
-            
-            self.imageView.frame = frame
-            
-        }) { finished in
-
-            if finished {
-                self.interactionInProgress = false
-                if self.gestureDirection == .horizontal {
-                    self.relativePosition.y = -1.0
-                } else {
-                    self.relativePosition.x = -1.0
-                }
-                self.finishTransition()
-            }
-        }
-    }
-
-    @objc private func update(_ timeInterval: TimeInterval) {
-
-        let speed = (Constants.updateFrameRate * Constants.transitionSpeedFactor)
-        let xDistance = distanceToMove.x / speed
-        let yDistance = distanceToMove.y / speed
-        distanceToMove.x -= xDistance
-        distanceToMove.y -= yDistance
-        relativePosition.x += xDistance
-        relativePosition.y += yDistance
-        updateTransition()
-
-        let translation = CGPoint(
-            x: xDistance * (UIScreen.main.bounds.size.width),
-            y: yDistance * (UIScreen.main.bounds.size.height)
-        )
-        let directionalTranslation = (gestureDirection == .horizontal) ? translation.y : translation.x
-        if abs(directionalTranslation) < 1.0 {
-
-            relativePosition.x += distanceToMove.x
-            relativePosition.y += distanceToMove.y
-            updateTransition()
-            interactionInProgress = false
-
-            finishTransition()
-        }
-    }
-
-    internal func beginTransition() {
-
-        shouldZoomOutOnInteraction = false
-        if let viewController = viewController {
-            shouldZoomOutOnInteraction = viewController.dataSource?.targetFrameForDismissal(viewController) != nil
-        }
-
-        createTransitionViews()
-
-        viewController?.mediaContainerView.isHidden = true
-    }
-
-    private func finishTransition() {
-
-        distanceToMove = .zero
-        timer?.invalidate()
-        timer = nil
-
-        imageView.removeFromSuperview()
-
-        let directionalPosition = (gestureDirection == .horizontal) ? relativePosition.y : relativePosition.x
-        if directionalPosition != 0.0 {
-            viewController?.dismiss(animated: false, completion: {
-                self.viewController?.delegate?.viewerImageViewControllerDismiss()
-            })
-        } else {
-            viewController?.mediaContainerView.isHidden = false
-        }
-    }
-
-    private func createTransitionViews() {
-
-        imageView.image = image
-        imageView.frame = imageViewFrame(
-            for: image?.size ?? .zero,
-            in: viewController?.view.bounds ?? .zero
-        )
-        viewController?.view.addSubview(imageView)
-        imageView.transform = CGAffineTransform.identity
-    }
-
-    private func updateTransition() {
-
-        var transform = CGAffineTransform.identity
-        let directionalPosition = (gestureDirection == .horizontal) ? relativePosition.y : relativePosition.x
-
-        if shouldZoomOutOnInteraction {
-            let scale = CGFloat.maximum(Constants.minimumZoomDuringInteraction, 1.0 - abs(directionalPosition))
-            transform = transform.scaledBy(x: scale, y: scale)
-        }
-
-        if gestureDirection == .horizontal {
-            transform = transform.translatedBy(
-                x: shouldZoomOutOnInteraction ? relativePosition.x * UIScreen.main.bounds.size.width : 0.0,
-                y: relativePosition.y * UIScreen.main.bounds.size.height
-            )
-        } else {
-            transform = transform.translatedBy(
-                x: relativePosition.x * UIScreen.main.bounds.size.width,
-                y: shouldZoomOutOnInteraction ? relativePosition.y * UIScreen.main.bounds.size.height : 0.0
-            )
-        }
-        imageView.transform = transform
-    }
-
-    private func imageViewFrame(for imageSize: CGSize, in frame: CGRect, mode: UIView.ContentMode = .scaleAspectFit) -> CGRect {
-
-        guard imageSize != .zero,
-            mode == .scaleAspectFit || mode == .scaleAspectFill else {
-            return frame
-        }
-
-        var targetImageSize = frame.size
-
-        let aspectHeight = frame.size.width / imageSize.width * imageSize.height
-        let aspectWidth = frame.size.height / imageSize.height * imageSize.width
-
-        if imageSize.width / imageSize.height > frame.size.width / frame.size.height {
-            if mode == .scaleAspectFit {
-                targetImageSize.height = aspectHeight
-            } else {
-                targetImageSize.width = aspectWidth
-            }
-        } else {
-            if mode == .scaleAspectFit {
-                targetImageSize.width = aspectWidth
-            } else {
-                targetImageSize.height = aspectHeight
-            }
-        }
-
-        let x = frame.minX + (frame.size.width - targetImageSize.width) / 2.0
-        let y = frame.minY + (frame.size.height - targetImageSize.height) / 2.0
-
-        return CGRect(origin: CGPoint(x: x, y: y), size: targetImageSize)
-    }
-}

+ 0 - 720
iOSClient/Viewer/NCViewerImage/NCViewerImageViewController.swift

@@ -1,720 +0,0 @@
-//
-//  NCViewerImageViewController.swift
-//  Nextcloud
-//
-//  Created by Suraj Thomas K on 7/10/18 Copyright © 2018 Al Tayer Group LLC..
-//  Modify for Nextcloud by Marino Faggiana on 04/03/2020.
-//
-//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
-//
-//  This program is free software: you can redistribute it and/or modify
-//  it under the terms of the GNU General Public License as published by
-//  the Free Software Foundation, either version 3 of the License, or
-//  (at your option) any later version.
-//
-//  This program is distributed in the hope that it will be useful,
-//  but WITHOUT ANY WARRANTY; without even the implied warranty of
-//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//  GNU General Public License for more details.
-//
-//  You should have received a copy of the GNU General Public License
-//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
-//
-
-protocol NCViewerImageViewControllerDataSource: class {
-   
-    func numberOfItems(in viewerImageViewController: NCViewerImageViewController) -> Int
-
-    func viewerImageViewController(_ viewerImageViewController: NCViewerImageViewController, imageAt index: Int, completion: @escaping (_ index: Int, _ image: UIImage?, _ metadata: tableMetadata, _ zoomScale: ZoomScale?, _ error: Error?) -> Void)
-
-    func targetFrameForDismissal(_ viewerImageViewController: NCViewerImageViewController) -> CGRect?
-}
-
-extension NCViewerImageViewControllerDataSource {
-
-    public func targetFrameForDismissal(_ viewerImageViewController: NCViewerImageViewController) -> CGRect? { return nil }
-}
-
-// MARK: - NCViewerImageViewControllerDelegate protocol
-
-protocol NCViewerImageViewControllerDelegate: class {
-
-    func viewerImageViewController(_ viewerImageViewController: NCViewerImageViewController, willChangeFocusTo index: Int, view: NCViewerImageContentView, metadata: tableMetadata)
-    func viewerImageViewController(_ viewerImageViewController: NCViewerImageViewController, didChangeFocusTo index: Int, view: NCViewerImageContentView, metadata: tableMetadata)
-
-    func viewerImageViewControllerTap(_ viewerImageViewController: NCViewerImageViewController, metadata: tableMetadata)
-    func viewerImageViewControllerLongPressBegan(_ viewerImageViewController: NCViewerImageViewController, metadata: tableMetadata)
-    func viewerImageViewControllerLongPressEnded(_ viewerImageViewController: NCViewerImageViewController, metadata: tableMetadata)
-
-    func viewerImageViewControllerDismiss()
-}
-
-extension NCViewerImageViewControllerDelegate {
-
-    func viewerImageViewController(_ viewerImageViewController: NCViewerImageViewController, didChangeFocusTo index: Int, view: NCViewerImageContentView, metadata: tableMetadata) {}
-    func viewerImageViewController(_ viewerImageViewController: NCViewerImageViewController, willChangeFocusTo index: Int, view: NCViewerImageContentView, metadata: tableMetadata) {}
-}
-
-public class NCViewerImageViewController: UIViewController {
-
-    // MARK: - Exposed Enumerations
-
-    public enum GestureDirection {
-
-        // Horizontal (left - right) gestures.
-        case horizontal
-        // Vertical (up - down) gestures.
-        case vertical
-    }
-
-    public enum BrowserStyle {
-
-        // Linear browser with *0* as first index and *numItems-1* as last index.
-        case linear
-        // Carousel browser. The media items are repeated in a circular fashion.
-        case carousel
-    }
-
-    public enum ContentDrawOrder {
-
-        // In this mode, media items are rendered in [previous]-[current]-[next] order.
-        case previousToNext
-        // In this mode, media items are rendered in [next]-[current]-[previous] order.
-        case nextToPrevious
-    }
-
-    // MARK: - Exposed variables
-
-    weak var dataSource: NCViewerImageViewControllerDataSource?
-    weak var delegate: NCViewerImageViewControllerDelegate?
-
-    var gestureDirection: GestureDirection = .horizontal
-
-    var contentTransformer: NCViewerImageContentTransformer = NCViewerImageDefaultContentTransformers.horizontalMoveInOut {
-        didSet {
-
-            NCViewerImageContentView.contentTransformer = contentTransformer
-            contentViews.forEach({ $0.updateTransform() })
-        }
-    }
-    
-    var drawOrder: ContentDrawOrder = .previousToNext {
-        didSet {
-            if oldValue != drawOrder {
-                mediaContainerView.exchangeSubview(at: 0, withSubviewAt: 2)
-            }
-        }
-    }
-    
-    var browserStyle: BrowserStyle = .carousel
-    // Gap between consecutive media items. Default is `50.0`.
-    
-    var gapBetweenMediaViews: CGFloat = Constants.gapBetweenContents {
-        didSet {
-            NCViewerImageContentView.interItemSpacing = gapBetweenMediaViews
-            contentViews.forEach({ $0.updateTransform() })
-        }
-    }
-    
-    // Enable or disable interactive dismissal. Default is enabled.
-    var enableInteractiveDismissal: Bool = true
-    
-    // Item index of the current item. In range `0..<numMediaItems`
-    var currentItemIndex: Int {
-        return sanitizeIndex(index)
-    }
-        
-    // MARK: - Private Enumerations
-
-    private enum Constants {
-
-        static let gapBetweenContents: CGFloat = 50.0
-        static let minimumVelocity: CGFloat = 15.0
-        static let minimumTranslation: CGFloat = 0.1
-        static let animationDuration = 0.3
-        static let updateFrameRate: CGFloat = 60.0
-        static let bounceFactor: CGFloat = 0.1
-
-        enum PageControl {
-
-            static let bottom: CGFloat = -10.0
-            static let tintColor: UIColor = .lightGray
-            static let selectedTintColor: UIColor = .white
-        }
-    }
-
-    // MARK: - Private variables
-    private(set) var index: Int = 0 {
-        didSet {
-            pageControl.currentPage = index
-        }
-    }
-
-    public var contentViews: [NCViewerImageContentView] = []
-
-    lazy private var tapGestureRecognizer: UITapGestureRecognizer = { [unowned self] in
-        let gesture = UITapGestureRecognizer()
-        gesture.numberOfTapsRequired = 1
-        gesture.numberOfTouchesRequired = 1
-        gesture.delegate = self
-        gesture.addTarget(self, action: #selector(tapGestureEvent(_:)))
-        return gesture
-    }()
-    
-    lazy private var longtapGestureRecognizer: UILongPressGestureRecognizer = { [unowned self] in
-        let gesture = UILongPressGestureRecognizer()
-        gesture.delaysTouchesBegan = true
-        gesture.minimumPressDuration = 0.3
-        gesture.delegate = self
-        gesture.addTarget(self, action: #selector(longpressGestureEvent(_:)))
-        return gesture
-    }()
-
-    private var previousTranslation: CGPoint = .zero
-
-    private var timer: Timer?
-    private var distanceToMove: CGFloat = 0.0
-
-    lazy private var panGestureRecognizer: UIPanGestureRecognizer = { [unowned self] in
-        let gesture = UIPanGestureRecognizer()
-        gesture.minimumNumberOfTouches = 1
-        gesture.maximumNumberOfTouches = 1
-        gesture.delegate = self
-        gesture.addTarget(self, action: #selector(panGestureEvent(_:)))
-        return gesture
-    }()
-
-    lazy internal private(set) var mediaContainerView: UIView = { [unowned self] in
-        let container = UIView()
-        container.backgroundColor = .clear
-        return container
-    }()
-    
-    lazy var statusView: UIImageView = {
-        let statusView = UIImageView()
-        statusView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
-        statusView.contentMode = .scaleAspectFit
-        statusView.clipsToBounds = true
-        return statusView
-    }()
-
-    lazy private var pageControl: UIPageControl = { [unowned self] in
-        let pageControl = UIPageControl()
-        pageControl.hidesForSinglePage = true
-        pageControl.numberOfPages = numMediaItems
-        pageControl.currentPageIndicatorTintColor = Constants.PageControl.selectedTintColor
-        pageControl.tintColor = Constants.PageControl.tintColor
-        pageControl.currentPage = index
-        return pageControl
-    }()
-
-    private var numMediaItems = 0
-
-    private lazy var dismissController = NCViewerImageDismissAnimationController(
-        gestureDirection: gestureDirection,
-        viewController: self
-    )
-
-    // MARK: - Public methods
-
-    public func reloadContentViews() {
-
-        numMediaItems = dataSource?.numberOfItems(in: self) ?? 0
-       
-        for contentView in contentViews {
-
-            updateContents(of: contentView)
-        }
-    }
-
-    // MARK: - Initializers
-
-    init(
-        index: Int = 0,
-        dataSource: NCViewerImageViewControllerDataSource,
-        delegate: NCViewerImageViewControllerDelegate? = nil
-        ) {
-
-        self.index = index
-        self.dataSource = dataSource
-        self.delegate = delegate
-
-        super.init(nibName: nil, bundle: nil)
-
-        initialize()
-    }
-
-    public required init?(coder aDecoder: NSCoder) {
-
-        super.init(coder: aDecoder)
-
-        initialize()
-    }
-
-    private func initialize() {
-
-        view.backgroundColor = .clear
-
-        modalPresentationStyle = .custom
-
-        modalTransitionStyle = .crossDissolve
-    }
-    
-    public func changeInViewSize(to size: CGSize) {
-        self.contentViews.forEach({ $0.handleChangeInViewSize(to: size) })
-    }
-}
-
-// MARK: - View Lifecycle and Events
-
-extension NCViewerImageViewController {
-
-    override public var prefersStatusBarHidden: Bool {
-
-        return true
-    }
-
-    override public func viewDidLoad() {
-
-        super.viewDidLoad()
-
-        numMediaItems = dataSource?.numberOfItems(in: self) ?? 0
-
-        populateContentViews()
-
-        view.addGestureRecognizer(panGestureRecognizer)
-        view.addGestureRecognizer(tapGestureRecognizer)
-        view.addGestureRecognizer(longtapGestureRecognizer)
-    }
-
-    override public func viewDidAppear(_ animated: Bool) {
-
-        super.viewDidAppear(animated)
-
-        contentViews.forEach({ $0.updateTransform() })
-    }
-
-    override public func viewWillDisappear(_ animated: Bool) {
-
-        super.viewWillDisappear(animated)
-    }
-    
-    public override func viewWillTransition(
-        to size: CGSize,
-        with coordinator: UIViewControllerTransitionCoordinator
-        ) {
-
-        coordinator.animate(alongsideTransition: { context in
-            self.contentViews.forEach({ $0.handleChangeInViewSize(to: size) })
-        }, completion: nil)
-
-        super.viewWillTransition(to: size, with: coordinator)
-    }
-
-    private func populateContentViews() {
-
-        view.addSubview(mediaContainerView)
-        mediaContainerView.translatesAutoresizingMaskIntoConstraints = false
-        NSLayoutConstraint.activate([
-            mediaContainerView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
-            mediaContainerView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
-            mediaContainerView.topAnchor.constraint(equalTo: view.topAnchor),
-            mediaContainerView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
-        ])
-        
-        view.addSubview(statusView)
-        statusView.translatesAutoresizingMaskIntoConstraints = false
-        
-        NSLayoutConstraint.activate([
-            statusView.widthAnchor.constraint(equalToConstant: 30),
-            statusView.heightAnchor.constraint(equalToConstant: 30),
-            statusView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 2),
-            statusView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 2)
-        ])
-       
-        statusView.setNeedsLayout()
-        statusView.layoutIfNeeded()
-
-        NCViewerImageContentView.interItemSpacing = gapBetweenMediaViews
-        NCViewerImageContentView.contentTransformer = contentTransformer
-
-        contentViews.forEach({ $0.removeFromSuperview() })
-        contentViews.removeAll()
-
-        for i in -1...1 {
-            let mediaView = NCViewerImageContentView(
-                index: i + index,
-                position: CGFloat(i),
-                frame: view.bounds
-            )
-            mediaContainerView.addSubview(mediaView)
-            mediaView.translatesAutoresizingMaskIntoConstraints = false
-            NSLayoutConstraint.activate([
-                mediaView.leadingAnchor.constraint(equalTo: mediaContainerView.leadingAnchor),
-                mediaView.trailingAnchor.constraint(equalTo: mediaContainerView.trailingAnchor),
-                mediaView.topAnchor.constraint(equalTo: mediaContainerView.topAnchor),
-                mediaView.bottomAnchor.constraint(equalTo: mediaContainerView.bottomAnchor)
-            ])
-
-            contentViews.append(mediaView)
-
-            if numMediaItems > 0 {
-                updateContents(of: mediaView)
-            }
-        }
-        if drawOrder == .nextToPrevious {
-            mediaContainerView.exchangeSubview(at: 0, withSubviewAt: 2)
-        }
-    }
-}
-
-// MARK: - Gesture Recognizers
-
-extension NCViewerImageViewController {
-
-    @objc private func panGestureEvent(_ recognizer: UIPanGestureRecognizer) {
-
-        if dismissController.interactionInProgress {
-            dismissController.handleInteractiveTransition(recognizer)
-            return
-        }
-
-        guard numMediaItems > 0 else {
-            return
-        }
-
-        let translation = recognizer.translation(in: view)
-
-        switch recognizer.state {
-        case .began:
-            previousTranslation = translation
-            distanceToMove = 0.0
-            timer?.invalidate()
-            timer = nil
-        case .changed:
-            moveViews(by: CGPoint(x: translation.x - previousTranslation.x, y: translation.y - previousTranslation.y))
-        case .ended, .failed, .cancelled:
-            let velocity = recognizer.velocity(in: view)
-
-            var viewsCopy = contentViews
-            let previousView = viewsCopy.removeFirst()
-            let middleView = viewsCopy.removeFirst()
-            let nextView = viewsCopy.removeFirst()
-
-            var toMove: CGFloat = 0.0
-            let directionalVelocity = gestureDirection == .horizontal ? velocity.x : velocity.y
-
-            if abs(directionalVelocity) < Constants.minimumVelocity &&
-                abs(middleView.position) < Constants.minimumTranslation {
-                toMove = -middleView.position
-            } else if directionalVelocity < 0.0 {
-                if middleView.position >= 0.0 {
-                    toMove = -middleView.position
-                } else {
-                    toMove = -nextView.position
-                }
-            } else {
-                if middleView.position <= 0.0 {
-                    toMove = -middleView.position
-                } else {
-                    toMove = -previousView.position
-                }
-            }
-
-            if browserStyle == .linear || numMediaItems <= 1 {
-                if (middleView.index == 0 && ((middleView.position + toMove) > 0.0)) ||
-                    (middleView.index == (numMediaItems - 1) && (middleView.position + toMove) < 0.0) {
-
-                    toMove = -middleView.position
-                }
-            }
-
-            distanceToMove = toMove
-
-            if timer == nil {
-                timer = Timer.scheduledTimer(
-                    timeInterval: 1.0/Double(Constants.updateFrameRate),
-                    target: self,
-                    selector: #selector(update(_:)),
-                    userInfo: nil,
-                    repeats: true
-                )
-            }
-        default:
-            break
-        }
-
-        previousTranslation = translation
-    }
-
-    @objc private func tapGestureEvent(_ recognizer: UITapGestureRecognizer) {
-
-        guard !dismissController.interactionInProgress else { return }
-        guard let mediaView = self.mediaView(at: 1) else { return }
-        
-        mediaView.zoomScaleOne()
-        
-        self.delegate?.viewerImageViewControllerTap(self, metadata: mediaView.metadata)
-    }
-    
-    @objc private func longpressGestureEvent(_ recognizer: UITapGestureRecognizer) {
-        
-        guard !dismissController.interactionInProgress else { return }
-        guard let mediaView = self.mediaView(at: 1) else { return }
-        
-        if recognizer.state == UIGestureRecognizer.State.began {
-            mediaView.zoomScaleOne()
-            self.delegate?.viewerImageViewControllerLongPressBegan(self, metadata: mediaView.metadata)
-        }
-        
-        if recognizer.state == UIGestureRecognizer.State.ended {
-            self.delegate?.viewerImageViewControllerLongPressEnded(self, metadata: mediaView.metadata)
-        }
-    }
-}
-
-// MARK: - Updating View Positions
-
-extension NCViewerImageViewController {
-
-    @objc private func update(_ timeInterval: TimeInterval) {
-
-        guard distanceToMove != 0.0 else {
-
-            timer?.invalidate()
-            timer = nil
-            return
-        }
-
-        let distance = distanceToMove / (Constants.updateFrameRate * 0.1)
-        distanceToMove -= distance
-        moveViewsNormalized(by: CGPoint(x: distance, y: distance))
-
-        let translation = CGPoint(
-            x: distance * (view.frame.size.width + gapBetweenMediaViews),
-            y: distance * (view.frame.size.height + gapBetweenMediaViews)
-        )
-        let directionalTranslation = (gestureDirection == .horizontal) ? translation.x : translation.y
-        if abs(directionalTranslation) < 0.1 {
-
-            moveViewsNormalized(by: CGPoint(x: distanceToMove, y: distanceToMove))
-            distanceToMove = 0.0
-            timer?.invalidate()
-            timer = nil
-        }
-    }
-
-    private func moveViews(by translation: CGPoint) {
-
-        let viewSizeIncludingGap = CGSize(
-            width: view.frame.size.width + gapBetweenMediaViews,
-            height: view.frame.size.height + gapBetweenMediaViews
-        )
-
-        let normalizedTranslation = calculateNormalizedTranslation(
-            translation: translation,
-            viewSize: viewSizeIncludingGap
-        )
-
-        moveViewsNormalized(by: normalizedTranslation)
-    }
-
-    private func moveViewsNormalized(by normalizedTranslation: CGPoint) {
-
-        let isGestureHorizontal = (gestureDirection == .horizontal)
-
-        contentViews.forEach({
-            $0.position += isGestureHorizontal ? normalizedTranslation.x : normalizedTranslation.y
-        })
-
-        var viewsCopy = contentViews
-        let previousView = viewsCopy.removeFirst()
-        let middleView = viewsCopy.removeFirst()
-        let nextView = viewsCopy.removeFirst()
-
-        let viewSizeIncludingGap = CGSize(
-            width: view.frame.size.width + gapBetweenMediaViews,
-            height: view.frame.size.height + gapBetweenMediaViews
-        )
-
-        let viewSize = isGestureHorizontal ? viewSizeIncludingGap.width : viewSizeIncludingGap.height
-        let normalizedGap = gapBetweenMediaViews/viewSize
-        let normalizedCenter = (middleView.frame.size.width / viewSize) * 0.5
-        let viewCount = contentViews.count
-
-        if middleView.position < -(normalizedGap + normalizedCenter) {
-
-            index = sanitizeIndex(index + 1)
-
-            // Previous item is taken and placed on right/down most side
-            previousView.position += CGFloat(viewCount)
-            previousView.index += viewCount
-            updateContents(of: previousView)
-
-            contentViews.removeFirst()
-            contentViews.append(previousView)
-
-            switch drawOrder {
-            case .previousToNext:
-                mediaContainerView.bringSubviewToFront(previousView)
-            case .nextToPrevious:
-                mediaContainerView.sendSubviewToBack(previousView)
-            }
-
-            delegate?.viewerImageViewController(self, willChangeFocusTo: index, view: nextView, metadata: nextView.metadata)
-
-        } else if middleView.position > (1 + normalizedGap - normalizedCenter) {
-
-            index = sanitizeIndex(index - 1)
-
-            // Next item is taken and placed on left/top most side
-            nextView.position -= CGFloat(viewCount)
-            nextView.index -= viewCount
-            updateContents(of: nextView)
-
-            contentViews.removeLast()
-            contentViews.insert(nextView, at: 0)
-
-            switch drawOrder {
-            case .previousToNext:
-                mediaContainerView.sendSubviewToBack(nextView)
-            case .nextToPrevious:
-                mediaContainerView.bringSubviewToFront(nextView)
-            }
-
-            delegate?.viewerImageViewController(self, willChangeFocusTo: index, view: previousView, metadata: previousView.metadata)
-            
-        } else if middleView.position == 0 {
-            
-            delegate?.viewerImageViewController(self, didChangeFocusTo: index, view: middleView, metadata: middleView.metadata)
-        }
-    }
-
-    private func calculateNormalizedTranslation(translation: CGPoint, viewSize: CGSize) -> CGPoint {
-
-        guard let middleView = mediaView(at: 1) else {
-            return .zero
-        }
-
-        var normalizedTranslation = CGPoint(
-            x: (translation.x)/viewSize.width,
-            y: (translation.y)/viewSize.height
-        )
-
-        if browserStyle != .carousel || numMediaItems <= 1 {
-            let isGestureHorizontal = (gestureDirection == .horizontal)
-            let directionalTranslation = isGestureHorizontal ? normalizedTranslation.x : normalizedTranslation.y
-            if (middleView.index == 0 && ((middleView.position + directionalTranslation) > 0.0)) ||
-                (middleView.index == (numMediaItems - 1) && (middleView.position + directionalTranslation) < 0.0) {
-                if isGestureHorizontal {
-                    normalizedTranslation.x *= Constants.bounceFactor
-                } else {
-                    normalizedTranslation.y *= Constants.bounceFactor
-                }
-            }
-        }
-        return normalizedTranslation
-    }
-
-    private func updateContents(of contentView: NCViewerImageContentView) {
-
-        contentView.image = nil
-        let convertedIndex = sanitizeIndex(contentView.index)
-        contentView.isLoading = true
-        dataSource?.viewerImageViewController(
-            self,
-            imageAt: convertedIndex,
-            completion: { [weak self] (index, image, metadata, zoom, _) in
-
-                guard let strongSelf = self else {
-                    return
-                }
-
-                if index == strongSelf.sanitizeIndex(contentView.index) {
-                    if image != nil {
-                        contentView.image = image
-                        contentView.metadata = metadata
-                        contentView.zoomLevels = zoom
-                    }
-                    contentView.isLoading = false
-                }
-            }
-        )
-    }
-
-    private func sanitizeIndex(_ index: Int) -> Int {
-
-        let newIndex = index % numMediaItems
-        if newIndex < 0 {
-            return newIndex + numMediaItems
-        }
-        return newIndex
-    }
-
-    private func sourceImage() -> UIImage? {
-
-        return mediaView(at: 1)?.image
-    }
-
-    private func mediaView(at index: Int) -> NCViewerImageContentView? {
-
-        guard index < contentViews.count else {
-
-            assertionFailure("Content views does not have this many views. : \(index)")
-            return nil
-        }
-        return contentViews[index]
-    }
-}
-
-// MARK: - UIGestureRecognizerDelegate
-
-extension NCViewerImageViewController: UIGestureRecognizerDelegate {
-
-    public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
-
-        guard enableInteractiveDismissal else {
-            return true
-        }
-
-        let middleView = mediaView(at: 1)
-        if middleView?.zoomScale == middleView?.zoomLevels?.minimumZoomScale,
-            let recognizer = gestureRecognizer as? UIPanGestureRecognizer {
-
-            let translation = recognizer.translation(in: recognizer.view)
-
-            if gestureDirection == .horizontal {
-                dismissController.interactionInProgress = abs(translation.y) > abs(translation.x)
-            } else {
-                dismissController.interactionInProgress = abs(translation.x) > abs(translation.y)
-            }
-            if dismissController.interactionInProgress {
-                dismissController.image = sourceImage()
-            }
-        }
-        return true
-    }
-
-    public func gestureRecognizer(
-        _ gestureRecognizer: UIGestureRecognizer,
-        shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer
-        ) -> Bool {
-
-        if gestureRecognizer is UIPanGestureRecognizer,
-            let scrollView = otherGestureRecognizer.view as? NCViewerImageContentView {
-            return scrollView.zoomScale == 1.0
-        }
-        return false
-    }
-
-    public func gestureRecognizer(
-        _ gestureRecognizer: UIGestureRecognizer,
-        shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer
-        ) -> Bool {
-
-        if gestureRecognizer is UITapGestureRecognizer {
-            return otherGestureRecognizer.view is NCViewerImageContentView
-        }
-        return false
-    }
-}