marinofaggiana 5 년 전
부모
커밋
1b46fa1808

+ 32 - 0
Nextcloud.xcodeproj/project.pbxproj

@@ -492,6 +492,12 @@
 		F7CA1ED720E7E3FE002CC65E /* PKDownloadButton.m in Sources */ = {isa = PBXBuildFile; fileRef = F7CA1EC820E7E3FE002CC65E /* PKDownloadButton.m */; };
 		F7CA1ED820E7E3FE002CC65E /* PKBorderedButton.m in Sources */ = {isa = PBXBuildFile; fileRef = F7CA1ECA20E7E3FE002CC65E /* PKBorderedButton.m */; };
 		F7CA1ED920E7E3FE002CC65E /* NSLayoutConstraint+PKDownloadButton.m in Sources */ = {isa = PBXBuildFile; fileRef = F7CA1ECE20E7E3FE002CC65E /* NSLayoutConstraint+PKDownloadButton.m */; };
+		F7CF2FFC240833EF005DE5F8 /* NCViewerImageView.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7CF2FF6240833EF005DE5F8 /* NCViewerImageView.xib */; };
+		F7CF2FFD240833EF005DE5F8 /* NCViewerImageAsset.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7CF2FF7240833EF005DE5F8 /* NCViewerImageAsset.swift */; };
+		F7CF2FFE240833EF005DE5F8 /* NCViewerImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7CF2FF8240833EF005DE5F8 /* NCViewerImageView.swift */; };
+		F7CF2FFF240833EF005DE5F8 /* NCViewerImageCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7CF2FF9240833EF005DE5F8 /* NCViewerImageCollectionViewCell.xib */; };
+		F7CF3000240833EF005DE5F8 /* NCViewerImageCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7CF2FFA240833EF005DE5F8 /* NCViewerImageCollectionViewCell.swift */; };
+		F7CF3001240833EF005DE5F8 /* NCViewerImageNibLoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7CF2FFB240833EF005DE5F8 /* NCViewerImageNibLoadingView.swift */; };
 		F7D1612023CF19E30039EBBF /* NCViewerRichWorkspace.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7D1611F23CF19E30039EBBF /* NCViewerRichWorkspace.storyboard */; };
 		F7D2D127230804E000FD3ED7 /* NCXMLListParser.m in Sources */ = {isa = PBXBuildFile; fileRef = F7D2D126230804E000FD3ED7 /* NCXMLListParser.m */; };
 		F7D2D128230804E000FD3ED7 /* NCXMLListParser.m in Sources */ = {isa = PBXBuildFile; fileRef = F7D2D126230804E000FD3ED7 /* NCXMLListParser.m */; };
@@ -1232,6 +1238,12 @@
 		F7CC04E61F5AD50D00378CEF /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
 		F7CE8AFA1DC1F8D8009CAE48 /* Nextcloud.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Nextcloud.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		F7CE8AFB1DC1F8D8009CAE48 /* Share.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = Share.appex; sourceTree = BUILT_PRODUCTS_DIR; };
+		F7CF2FF6240833EF005DE5F8 /* NCViewerImageView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCViewerImageView.xib; sourceTree = "<group>"; };
+		F7CF2FF7240833EF005DE5F8 /* NCViewerImageAsset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCViewerImageAsset.swift; sourceTree = "<group>"; };
+		F7CF2FF8240833EF005DE5F8 /* NCViewerImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCViewerImageView.swift; sourceTree = "<group>"; };
+		F7CF2FF9240833EF005DE5F8 /* NCViewerImageCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCViewerImageCollectionViewCell.xib; sourceTree = "<group>"; };
+		F7CF2FFA240833EF005DE5F8 /* NCViewerImageCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCViewerImageCollectionViewCell.swift; sourceTree = "<group>"; };
+		F7CF2FFB240833EF005DE5F8 /* NCViewerImageNibLoadingView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCViewerImageNibLoadingView.swift; sourceTree = "<group>"; };
 		F7D154271E2392A300202FD9 /* Nextcloud-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Nextcloud-Bridging-Header.h"; sourceTree = "<group>"; };
 		F7D1611F23CF19E30039EBBF /* NCViewerRichWorkspace.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCViewerRichWorkspace.storyboard; sourceTree = "<group>"; };
 		F7D2D125230804DF00FD3ED7 /* NCXMLListParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NCXMLListParser.h; sourceTree = "<group>"; };
@@ -2168,6 +2180,7 @@
 		F79630EC215526B60015EEA5 /* Viewer */ = {
 			isa = PBXGroup;
 			children = (
+				F7CF2FF5240833EF005DE5F8 /* NCViewerImage */,
 				F72D404823D2082500A97FD0 /* NCViewerNextcloudText.swift */,
 				F710D1F42405770F00A6033D /* NCViewerPDF.swift */,
 				F790110D21415BF600D7B136 /* NCViewerRichdocument.swift */,
@@ -2454,6 +2467,19 @@
 			path = RichWorkspace;
 			sourceTree = "<group>";
 		};
+		F7CF2FF5240833EF005DE5F8 /* NCViewerImage */ = {
+			isa = PBXGroup;
+			children = (
+				F7CF2FF6240833EF005DE5F8 /* NCViewerImageView.xib */,
+				F7CF2FF7240833EF005DE5F8 /* NCViewerImageAsset.swift */,
+				F7CF2FF8240833EF005DE5F8 /* NCViewerImageView.swift */,
+				F7CF2FF9240833EF005DE5F8 /* NCViewerImageCollectionViewCell.xib */,
+				F7CF2FFA240833EF005DE5F8 /* NCViewerImageCollectionViewCell.swift */,
+				F7CF2FFB240833EF005DE5F8 /* NCViewerImageNibLoadingView.swift */,
+			);
+			path = NCViewerImage;
+			sourceTree = "<group>";
+		};
 		F7D4B67A22956610000C2C86 /* Firebase */ = {
 			isa = PBXGroup;
 			children = (
@@ -3025,6 +3051,7 @@
 				F7F54CF31E5B14C700E19C62 /* ImageSelectedSmallOn@3x.png in Resources */,
 				F7F54CFA1E5B14C700E19C62 /* UIBarButtonItemArrowLeft.png in Resources */,
 				F7FCFFD81D70798C000E6E29 /* CCPeekPop.storyboard in Resources */,
+				F7CF2FFF240833EF005DE5F8 /* NCViewerImageCollectionViewCell.xib in Resources */,
 				F7F54CF61E5B14C700E19C62 /* PlayButtonOverlayLarge@3x.png in Resources */,
 				F78F74342163757000C2ADAD /* NCTrash.storyboard in Resources */,
 				F760F78521F21F61006B1A73 /* EmojiCollectionViewCell.xib in Resources */,
@@ -3100,6 +3127,7 @@
 				F7AE00FA230E81EB007ACF8A /* NCBrowserWeb.storyboard in Resources */,
 				F7F54CE91E5B14C700E19C62 /* ImageSelectedOff@2x.png in Resources */,
 				F77B0F8A1D118A16002130FE /* CCCellMain.xib in Resources */,
+				F7CF2FFC240833EF005DE5F8 /* NCViewerImageView.xib in Resources */,
 				F78ACD58219048040088454D /* NCSectionHeaderMenu.xib in Resources */,
 				F77B0F8C1D118A16002130FE /* CCCellMainTransfer.xib in Resources */,
 				F73B4EF41F470D9100BBEE4B /* JISFreq.tab in Resources */,
@@ -3382,6 +3410,7 @@
 				F769454022E9F077000A798A /* NCSharePaging.swift in Sources */,
 				F760F79921F21F61006B1A73 /* CropViewController.swift in Sources */,
 				F70022EC1EC4C9100080073F /* OCXMLSharedParser.m in Sources */,
+				F7CF2FFD240833EF005DE5F8 /* NCViewerImageAsset.swift in Sources */,
 				F7F54D061E5B14C800E19C62 /* MWCaptionView.m in Sources */,
 				F762CB001EACB66200B38484 /* XLFormSegmentedCell.m in Sources */,
 				F732BA061D76CE1500E9878B /* CCNetworking.m in Sources */,
@@ -3441,6 +3470,7 @@
 				F7D6650720FF341600BFBA9E /* NCMainCommon.swift in Sources */,
 				F700510522DF6A89003A3356 /* NCShare.swift in Sources */,
 				F762CAF81EACB66200B38484 /* XLFormButtonCell.m in Sources */,
+				F7CF3000240833EF005DE5F8 /* NCViewerImageCollectionViewCell.swift in Sources */,
 				F7CA1ED120E7E3FE002CC65E /* PKCircleProgressView.m in Sources */,
 				F762CAFC1EACB66200B38484 /* XLFormImageCell.m in Sources */,
 				F72D1007210B6882009C96B7 /* NCPushNotificationEncryption.m in Sources */,
@@ -3519,6 +3549,7 @@
 				F76B3CCE1EAE01BD00921AC9 /* NCBrand.swift in Sources */,
 				F7BF1B431D51E893000854F6 /* CCLogin.m in Sources */,
 				F760F79821F21F61006B1A73 /* StickerCollectionViewCell.swift in Sources */,
+				F7CF2FFE240833EF005DE5F8 /* NCViewerImageView.swift in Sources */,
 				F70022FB1EC4C9100080073F /* NSString+Encode.m in Sources */,
 				F769454422E9F142000A798A /* NCShareUserMenuView.swift in Sources */,
 				F75037511DBFA91A008FB480 /* NSLayoutConstraint+PureLayout.m in Sources */,
@@ -3570,6 +3601,7 @@
 				F7AE00F5230D5F9E007ACF8A /* NCLoginWeb.swift in Sources */,
 				F73B4F0C1F470D9100BBEE4B /* nsHebrewProber.cpp in Sources */,
 				F762CAFB1EACB66200B38484 /* XLFormDatePickerCell.m in Sources */,
+				F7CF3001240833EF005DE5F8 /* NCViewerImageNibLoadingView.swift in Sources */,
 				F762CB0F1EACB66200B38484 /* NSObject+XLFormAdditions.m in Sources */,
 				F7B2DEF01F976854007CF4D2 /* NYMnemonic.m in Sources */,
 				F707C26521A2DC5200F6181E /* NCStoreReview.swift in Sources */,

+ 8 - 8
iOSClient/Main/Main.storyboard

@@ -161,23 +161,24 @@
                         <rect key="frame" x="0.0" y="0.0" width="414" height="808"/>
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                         <subviews>
-                            <imageView tag="999" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" preservesSuperviewLayoutMargins="YES" image="logo" translatesAutoresizingMaskIntoConstraints="NO" id="zlU-MP-ZVs">
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="xN5-L2-jLn" customClass="NCViewerImageView" customModule="Nextcloud" customModuleProvider="target">
                                 <rect key="frame" x="0.0" y="0.0" width="414" height="774"/>
-                            </imageView>
+                                <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
+                            </view>
                         </subviews>
                         <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                         <constraints>
-                            <constraint firstItem="3ph-Od-2hO" firstAttribute="trailing" secondItem="zlU-MP-ZVs" secondAttribute="trailing" id="GuY-bH-GIn"/>
-                            <constraint firstItem="zlU-MP-ZVs" firstAttribute="top" secondItem="3ph-Od-2hO" secondAttribute="top" id="HSh-HF-TAJ"/>
-                            <constraint firstItem="3ph-Od-2hO" firstAttribute="bottom" secondItem="zlU-MP-ZVs" secondAttribute="bottom" id="hvN-HY-D9V"/>
-                            <constraint firstItem="zlU-MP-ZVs" firstAttribute="leading" secondItem="3ph-Od-2hO" secondAttribute="leading" id="sVy-C6-hu1"/>
+                            <constraint firstItem="3ph-Od-2hO" firstAttribute="trailing" secondItem="xN5-L2-jLn" secondAttribute="trailing" id="WLC-fG-SGo"/>
+                            <constraint firstItem="xN5-L2-jLn" firstAttribute="top" secondItem="3ph-Od-2hO" secondAttribute="top" id="aUB-Ga-Gl0"/>
+                            <constraint firstItem="3ph-Od-2hO" firstAttribute="bottom" secondItem="xN5-L2-jLn" secondAttribute="bottom" id="wpM-zu-xgz"/>
+                            <constraint firstItem="xN5-L2-jLn" firstAttribute="leading" secondItem="3ph-Od-2hO" secondAttribute="leading" id="zOj-hc-1Yb"/>
                         </constraints>
                         <viewLayoutGuide key="safeArea" id="3ph-Od-2hO"/>
                     </view>
                     <extendedEdge key="edgesForExtendedLayout"/>
                     <navigationItem key="navigationItem" id="cJm-UN-Dvj" userLabel="Detail"/>
                     <connections>
-                        <outlet property="backgroundView" destination="zlU-MP-ZVs" id="yVd-Vz-gsJ"/>
+                        <outlet property="viewerImageView" destination="xN5-L2-jLn" id="7Cv-7o-KW2"/>
                     </connections>
                 </viewController>
                 <placeholder placeholderIdentifier="IBFirstResponder" id="IJ0-oL-QKv" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -649,7 +650,6 @@
     <resources>
         <image name="avatar" width="25" height="25"/>
         <image name="disclosureIndicator" width="26" height="28"/>
-        <image name="logo" width="223" height="157.5"/>
         <image name="tabBarPlus" width="80" height="80"/>
     </resources>
     <inferredMetricsTieBreakers>

+ 30 - 23
iOSClient/Main/NCDetailViewController.swift

@@ -27,7 +27,7 @@ import NCCommunication
 
 class NCDetailViewController: UIViewController {
     
-    @IBOutlet weak var backgroundView: UIImageView!
+    @IBOutlet weak var viewerImageView: NCViewerImageView!
     
     @objc var metadata: tableMetadata?
     @objc var selector: String?
@@ -71,7 +71,7 @@ class NCDetailViewController: UIViewController {
                     navigationController.popToRootViewController(animated: true)
                 }
             } else {
-                for view in backgroundView.subviews {
+                for view in viewerImageView.subviews {
                     view.removeFromSuperview()
                 }
                 self.navigationController?.navigationBar.topItem?.title = ""
@@ -80,12 +80,12 @@ class NCDetailViewController: UIViewController {
     }
     
     @objc func changeTheming() {
-        backgroundView.image = CCGraphics.changeThemingColorImage(UIImage.init(named: "logo"), multiplier: 2, color: NCBrandColor.sharedInstance.brand.withAlphaComponent(0.4))
+        //backgroundView.image = CCGraphics.changeThemingColorImage(UIImage.init(named: "logo"), multiplier: 2, color: NCBrandColor.sharedInstance.brand.withAlphaComponent(0.4))
         view.backgroundColor = NCBrandColor.sharedInstance.backgroundView
     }
     
     func subViewActive() -> UIView? {
-        return backgroundView.subviews.first
+        return viewerImageView.subviews.first
     }
     
     @objc func viewFile(metadata: tableMetadata, selector: String?) {
@@ -106,22 +106,29 @@ class NCDetailViewController: UIViewController {
         
         // IMAGE
         if metadata.typeFile == k_metadataTypeFile_image {
-            let viewerPhotoViewController = NCViewerPhotoViewController()
-            self.addChild(viewerPhotoViewController)
-            self.backgroundView.addSubview(viewerPhotoViewController.view)
-            viewerPhotoViewController.didMove(toParent: self)
+            if let metadatas = NCManageDatabase.sharedInstance.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND typeFile == %@", metadata.account, metadata.serverUrl, k_metadataTypeFile_image), sorted: "fileName", ascending: true) {
+                var assets: [NCViewerImageAsset?] = [NCViewerImageAsset]()
+                for metadata in metadatas {
+                    let imagePath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!
+                    if let image = UIImage(contentsOfFile: imagePath) {
+                        let asset = NCViewerImageAsset(image: image)
+                        assets.append(asset)
+                    }
+                }
+                viewerImageView.assets = assets
+            }
             return
         }
         
         // AUDIO VIDEO
         if metadata.typeFile == k_metadataTypeFile_audio || metadata.typeFile == k_metadataTypeFile_video {
-            NCViewerMedia.sharedInstance.viewMedia(metadata, view: backgroundView)
+            NCViewerMedia.sharedInstance.viewMedia(metadata, view: viewerImageView)
             return
         }
         
         // DOCUMENT - INTERNAL VIEWER
         if metadata.typeFile == k_metadataTypeFile_document && selector != nil && selector == selectorLoadFileInternalView {
-            NCViewerDocumentWeb.sharedInstance.viewDocumentWebAt(metadata, view: backgroundView)
+            NCViewerDocumentWeb.sharedInstance.viewDocumentWebAt(metadata, view: viewerImageView)
             return
         }
         
@@ -131,14 +138,14 @@ class NCDetailViewController: UIViewController {
             // PDF
             if metadata.contentType == "application/pdf" {
                 if #available(iOS 11.0, *) {
-                    let viewerPDF = NCViewerPDF.init(frame: backgroundView.frame)
+                    let viewerPDF = NCViewerPDF.init(frame: viewerImageView.frame)
                     
                     let filePath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!
                     if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) == false {
                         return
                     }
                     
-                    viewerPDF.setupPdfView(filePath: URL(fileURLWithPath: filePath), view: backgroundView)
+                    viewerPDF.setupPdfView(filePath: URL(fileURLWithPath: filePath), view: viewerImageView)
                 }
                 
                 return
@@ -150,7 +157,7 @@ class NCDetailViewController: UIViewController {
                 let editor = NCUtility.sharedInstance.isDirectEditing(metadata)!
                 if editor == k_editor_text || editor == k_editor_onlyoffice {
                     
-                    NCUtility.sharedInstance.startActivityIndicator(view: backgroundView, bottom: 0)
+                    NCUtility.sharedInstance.startActivityIndicator(view: viewerImageView, bottom: 0)
 
                     if metadata.url == "" {
                         
@@ -165,8 +172,8 @@ class NCDetailViewController: UIViewController {
                             
                             if errorCode == 0 && account == self.appDelegate.activeAccount && url != nil {
                                 
-                                let nextcloudText = NCViewerNextcloudText.init(frame: self.backgroundView.frame, configuration: WKWebViewConfiguration())
-                                nextcloudText.viewerAt(url!, metadata: metadata, editor: editor, view: self.backgroundView, viewController: self)
+                                let nextcloudText = NCViewerNextcloudText.init(frame: self.viewerImageView.frame, configuration: WKWebViewConfiguration())
+                                nextcloudText.viewerAt(url!, metadata: metadata, editor: editor, view: self.viewerImageView, viewController: self)
                                 if editor == k_editor_text && self.splitViewController!.isCollapsed {
                                     self.navigationController?.navigationItem.hidesBackButton = true
                                 }
@@ -184,8 +191,8 @@ class NCDetailViewController: UIViewController {
                         
                     } else {
                         
-                        let nextcloudText = NCViewerNextcloudText.init(frame: backgroundView.frame, configuration: WKWebViewConfiguration())
-                        nextcloudText.viewerAt(metadata.url, metadata: metadata, editor: editor, view: backgroundView, viewController: self)
+                        let nextcloudText = NCViewerNextcloudText.init(frame: viewerImageView.frame, configuration: WKWebViewConfiguration())
+                        nextcloudText.viewerAt(metadata.url, metadata: metadata, editor: editor, view: viewerImageView, viewController: self)
                         if editor == k_editor_text && self.splitViewController!.isCollapsed {
                             self.navigationController?.navigationItem.hidesBackButton = true
                         }
@@ -198,7 +205,7 @@ class NCDetailViewController: UIViewController {
             // RichDocument: Collabora
             if NCUtility.sharedInstance.isRichDocument(metadata) && appDelegate.reachability.isReachable() {
                 
-                NCUtility.sharedInstance.startActivityIndicator(view: backgroundView, bottom: 0)
+                NCUtility.sharedInstance.startActivityIndicator(view: viewerImageView, bottom: 0)
                 
                 if metadata.url == "" {
                     
@@ -206,8 +213,8 @@ class NCDetailViewController: UIViewController {
                         
                         if errorCode == 0 && account == self.appDelegate.activeAccount && url != nil {
                             
-                            let richDocument = NCViewerRichdocument.init(frame: self.backgroundView.frame, configuration: WKWebViewConfiguration())
-                            richDocument.viewRichDocumentAt(url!, metadata: metadata, view: self.backgroundView, viewController: self)
+                            let richDocument = NCViewerRichdocument.init(frame: self.viewerImageView.frame, configuration: WKWebViewConfiguration())
+                            richDocument.viewRichDocumentAt(url!, metadata: metadata, view: self.viewerImageView, viewController: self)
                             if self.splitViewController != nil && self.splitViewController!.isCollapsed {
                                 self.navigationController?.navigationItem.hidesBackButton = true
                             }
@@ -226,8 +233,8 @@ class NCDetailViewController: UIViewController {
                     
                 } else {
                     
-                    let richDocument = NCViewerRichdocument.init(frame: backgroundView.frame, configuration: WKWebViewConfiguration())
-                    richDocument.viewRichDocumentAt(metadata.url, metadata: metadata, view: backgroundView, viewController: self)
+                    let richDocument = NCViewerRichdocument.init(frame: viewerImageView.frame, configuration: WKWebViewConfiguration())
+                    richDocument.viewRichDocumentAt(metadata.url, metadata: metadata, view: viewerImageView, viewController: self)
                     if self.splitViewController != nil && self.splitViewController!.isCollapsed {
                         self.navigationController?.navigationItem.hidesBackButton = true
                     }
@@ -236,7 +243,7 @@ class NCDetailViewController: UIViewController {
         }
         
         // OTHER
-        NCViewerDocumentWeb.sharedInstance.viewDocumentWebAt(metadata, view: backgroundView)
+        NCViewerDocumentWeb.sharedInstance.viewDocumentWebAt(metadata, view: viewerImageView)
     }
 
 }

+ 78 - 0
iOSClient/Viewer/NCViewerImage/NCViewerImageAsset.swift

@@ -0,0 +1,78 @@
+
+import UIKit
+
+public class NCViewerImageAsset: NSObject {
+
+    public enum ImageType {
+        case jpg
+        case gif
+
+        static func from(mimeType: String) -> ImageType? {
+            if mimeType.contains("gif") { return .gif
+            } else if mimeType.contains("jpg") { return .jpg }
+            return nil
+        }
+    }
+
+    public var url: URL?
+    public var image: UIImage?
+    public var type: ImageType?
+    public var caption: String?
+
+    private override init() { }
+
+    public init(url: URL) {
+        self.url = url
+    }
+
+    public init(url: URL, caption: String?) {
+        self.url = url
+        self.caption = caption
+    }
+
+    public init(image: UIImage) {
+        self.image = image
+    }
+
+    public init(image: UIImage, caption: String?) {
+        self.image = image
+        self.caption = caption
+    }
+
+    func download(completion:@escaping(_ success: Bool?) -> Void) -> URLSessionDataTask? {
+        return NCViewerImageAsset.download(url: url) { (success, image, type)  in
+            self.image = image
+            if let type = type {
+                self.type = type
+            }
+            completion(success)
+        }
+    }
+
+    static func download(url: URL?, completion:@escaping(_ success: Bool?, _ image: UIImage?, _ type: NCViewerImageAsset.ImageType?) -> Void) -> URLSessionDataTask? {
+        guard let url = url else {
+            completion(false, nil, nil)
+            return nil
+        }
+        let dataTask = URLSession.shared.dataTask(with: url) { data, response, error  in
+            guard let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
+                let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
+                let data = data, error == nil,
+                var image = UIImage(data: data)
+                else {
+                    DispatchQueue.main.async { completion(false, nil, nil) }
+                    return
+            }
+            let type: NCViewerImageAsset.ImageType? = ImageType.from(mimeType: mimeType)
+            /*
+            if type == .gif, let gif = UIImage.gif(data: data) {
+                image = gif
+            }
+            */
+            DispatchQueue.main.async { completion(true, image, type) }
+        }
+        dataTask.resume()
+        return dataTask
+    }
+
+}

+ 145 - 0
iOSClient/Viewer/NCViewerImage/NCViewerImageCollectionViewCell.swift

@@ -0,0 +1,145 @@
+
+import UIKit
+
+protocol NCViewerImageCollectionViewCellDelegate: class {
+    func didStartZooming(_ cell: NCViewerImageCollectionViewCell)
+}
+
+class NCViewerImageCollectionViewCell: UICollectionViewCell {
+    static var reusableIdentifier: String = "NCViewerImageCollectionViewCell"
+
+    private var dataTask: URLSessionDataTask?
+    weak var delegate: NCViewerImageCollectionViewCellDelegate?
+
+    @IBOutlet weak var imageWidthConstraint: NSLayoutConstraint!
+    @IBOutlet weak var imageHeightConstraint: NSLayoutConstraint!
+    @IBOutlet weak var topConstraint: NSLayoutConstraint!
+    @IBOutlet weak var bottomConstraint: NSLayoutConstraint!
+    @IBOutlet weak var scrollView: UIScrollView!
+    @IBOutlet weak var galleryImageView: UIImageView!
+    @IBOutlet weak var leadingConstraint: NSLayoutConstraint!
+    @IBOutlet weak var trailingConstraint: NSLayoutConstraint!
+
+    private var observer: NSKeyValueObservation?
+
+    func withImageAsset(_ asset: NCViewerImageAsset?) {
+        guard self.dataTask?.state != URLSessionDataTask.State.running else { return }
+        guard let asset = asset else { return }
+        if asset.image != nil {
+            self.apply(image: self.fitIntoFrame(image: asset.image, type: asset.type))
+        } else if asset.url != nil {
+            self.galleryImageView.image = nil
+            self.dataTask = asset.download(completion: { _ in
+                self.apply(image: self.fitIntoFrame(image: asset.image, type: asset.type))
+            })
+        }
+    }
+
+    func apply(image: UIImage?) {
+        guard let image = image else { return }
+        self.galleryImageView.alpha = 0
+        self.galleryImageView.image = image
+        UIView.animate(withDuration: 0.1) {
+            self.galleryImageView.alpha = 1
+        }
+    }
+
+    override func draw(_ rect: CGRect) {
+        super.draw(rect)
+        self.scrollView.maximumZoomScale = 4
+        self.redrawConstraintIfNeeded()
+        self.observer = self.observe(\.bounds, options: NSKeyValueObservingOptions.new, changeHandler: { (_, _) in
+            self.apply(image: self.fitIntoFrame(image: self.galleryImageView.image, type: nil))
+            self.redrawConstraintIfNeeded()
+        })
+    }
+
+    func cancelPendingDataTask() {
+        self.dataTask?.cancel()
+    }
+
+    override func layoutSubviews() {
+        DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
+            self.redrawConstraintIfNeeded()
+        }
+        super.layoutSubviews()
+    }
+
+    override func prepareForReuse() {
+        super.prepareForReuse()
+        self.scrollView.setZoomScale(1, animated: false)
+        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
+            // limitation of cell lifecycle
+            self.redrawConstraintIfNeeded()
+        }
+    }
+
+    func setMargins(vertical: CGFloat, horizontal: CGFloat) {
+        self.topConstraint.constant = vertical
+        self.bottomConstraint.constant = vertical
+        self.leadingConstraint.constant = horizontal
+        self.trailingConstraint.constant = horizontal
+    }
+
+    func redrawConstraintIfNeeded() {
+        let imageHeight = self.galleryImageView.frame.size.height
+        let imageWidth = self.galleryImageView.frame.size.width
+        let spaceLeftVertical = self.scrollView.frame.size.height-imageHeight
+        let spaceLeftHorizontal = self.scrollView.frame.size.width-imageWidth
+        let constraintConstantValueVertical = spaceLeftVertical/2 > 0 ? spaceLeftVertical/2 : 0
+        let constraintConstantValueHorizontal = spaceLeftHorizontal/2 > 0 ? spaceLeftHorizontal/2 : 0
+        self.setMargins(vertical: constraintConstantValueVertical, horizontal: constraintConstantValueHorizontal)
+        self.layoutIfNeeded()
+    }
+
+    private func fitIntoFrame(image: UIImage?, type: NCViewerImageAsset.ImageType?) -> UIImage? {
+        let type: NCViewerImageAsset.ImageType = type ?? .jpg
+        guard let image = image else { return nil }
+        guard image.size != CGSize.zero else { return nil }
+        let screenRatio = UIScreen.main.bounds.size.width/UIScreen.main.bounds.size.height
+        var reqWidth: CGFloat = frame.size.width
+        if image.size.width > reqWidth {
+            reqWidth = image.size.width
+        }
+        let imageRatio = image.size.width/image.size.height
+        if imageRatio < screenRatio {
+            reqWidth = frame.size.height*imageRatio
+        }
+        let size = CGSize(width: reqWidth, height: reqWidth/imageRatio)
+        if imageRatio < screenRatio {
+            self.imageHeightConstraint.constant = frame.size.height
+            self.imageWidthConstraint.constant = frame.size.height*imageRatio
+        } else {
+            self.imageHeightConstraint.constant = frame.size.width/imageRatio
+            self.imageWidthConstraint.constant = frame.size.width
+        }
+        switch type {
+        case .gif: return image
+        case .jpg:
+            UIGraphicsBeginImageContext(size)
+            image.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
+            let finalImage: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
+            UIGraphicsEndImageContext()
+            return finalImage
+        }
+    }
+
+    func redrawImage() {
+        self.apply(image: self.fitIntoFrame(image: self.galleryImageView.image, type: nil))
+        self.redrawConstraintIfNeeded()
+    }
+}
+
+extension NCViewerImageCollectionViewCell: UIScrollViewDelegate {
+    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
+        return self.galleryImageView
+    }
+
+    func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) {
+        self.delegate?.didStartZooming(self)
+    }
+
+    func scrollViewDidZoom(_ scrollView: UIScrollView) {
+        self.redrawConstraintIfNeeded()
+    }
+}

+ 68 - 0
iOSClient/Viewer/NCViewerImage/NCViewerImageCollectionViewCell.xib

@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15705" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15706"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="gTV-IL-0wX" customClass="NCViewerImageCollectionViewCell" customModule="Nextcloud" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="372" height="458"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
+                <rect key="frame" x="0.0" y="0.0" width="372" height="458"/>
+                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                <subviews>
+                    <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fYl-gF-3Tc">
+                        <rect key="frame" x="0.0" y="0.0" width="372" height="458"/>
+                        <subviews>
+                            <imageView contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="3" translatesAutoresizingMaskIntoConstraints="NO" id="Y2R-g3-1to">
+                                <rect key="frame" x="0.0" y="0.0" width="372" height="375"/>
+                                <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="375" id="buA-yt-EU1"/>
+                                    <constraint firstAttribute="width" constant="372" id="dup-Ci-FrN"/>
+                                </constraints>
+                            </imageView>
+                        </subviews>
+                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <constraints>
+                            <constraint firstAttribute="trailing" secondItem="Y2R-g3-1to" secondAttribute="trailing" priority="250" id="Q4y-Pe-Blg"/>
+                            <constraint firstItem="Y2R-g3-1to" firstAttribute="top" secondItem="fYl-gF-3Tc" secondAttribute="top" priority="250" id="S31-PI-s22"/>
+                            <constraint firstItem="Y2R-g3-1to" firstAttribute="leading" secondItem="fYl-gF-3Tc" secondAttribute="leading" priority="250" id="l8D-LP-ERM"/>
+                            <constraint firstAttribute="bottom" secondItem="Y2R-g3-1to" secondAttribute="bottom" priority="250" id="u4J-cI-jJ1"/>
+                        </constraints>
+                        <connections>
+                            <outlet property="delegate" destination="gTV-IL-0wX" id="4Eo-bw-Wov"/>
+                        </connections>
+                    </scrollView>
+                </subviews>
+            </view>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstAttribute="bottom" secondItem="fYl-gF-3Tc" secondAttribute="bottom" id="EGd-Qa-dtz"/>
+                <constraint firstItem="fYl-gF-3Tc" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" id="RSp-db-iCz"/>
+                <constraint firstItem="fYl-gF-3Tc" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="cgT-tQ-4MD"/>
+                <constraint firstAttribute="trailing" secondItem="fYl-gF-3Tc" secondAttribute="trailing" id="plj-M5-9uV"/>
+            </constraints>
+            <size key="customSize" width="372" height="458"/>
+            <connections>
+                <outlet property="bottomConstraint" destination="u4J-cI-jJ1" id="apG-eS-WzL"/>
+                <outlet property="galleryImageView" destination="Y2R-g3-1to" id="uWL-Dw-oXx"/>
+                <outlet property="imageHeightConstraint" destination="buA-yt-EU1" id="OGS-lC-INp"/>
+                <outlet property="imageWidthConstraint" destination="dup-Ci-FrN" id="8cX-Dl-FHP"/>
+                <outlet property="leadingConstraint" destination="l8D-LP-ERM" id="rQa-OZ-pFo"/>
+                <outlet property="scrollView" destination="fYl-gF-3Tc" id="veY-z3-1mI"/>
+                <outlet property="topConstraint" destination="S31-PI-s22" id="DO9-Zk-F38"/>
+                <outlet property="trailingConstraint" destination="Q4y-Pe-Blg" id="hrk-zy-L3s"/>
+            </connections>
+            <point key="canvasLocation" x="195" y="258"/>
+        </collectionViewCell>
+    </objects>
+    <resources>
+        <image name="3" width="592.5" height="1555.5"/>
+    </resources>
+</document>

+ 42 - 0
iOSClient/Viewer/NCViewerImage/NCViewerImageNibLoadingView.swift

@@ -0,0 +1,42 @@
+
+import UIKit
+
+@IBDesignable
+public class NCViewerImageNibLoadingView: UIView {
+
+    @IBOutlet weak var view: UIView!
+
+    public override init(frame: CGRect) {
+        super.init(frame: frame)
+        view = NCViewerImageNibLoading.nibSetup(self)
+    }
+
+    public required init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+        view = NCViewerImageNibLoading.nibSetup(self)
+    }
+
+    public override func layoutSubviews() {
+        super.layoutSubviews()
+        self.view.backgroundColor = .clear
+    }
+}
+
+private class NCViewerImageNibLoading: NSObject {
+    class func loadViewFromNib(_ obj: UIView) -> UIView {
+        let bundle = Bundle(for: type(of: obj))
+        let nib = UINib(nibName: String(describing: type(of: obj)), bundle: bundle)
+        let nibView = (nib.instantiate(withOwner: obj, options: nil).first as? UIView)!
+        return nibView
+    }
+
+    class func nibSetup(_ obj: UIView) -> UIView {
+        obj.backgroundColor = .clear
+        let view: UIView = NCViewerImageNibLoading.loadViewFromNib(obj)
+        view.frame = obj.bounds
+        view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+        view.translatesAutoresizingMaskIntoConstraints = true
+        obj.addSubview(view)
+        return view
+    }
+}

+ 77 - 0
iOSClient/Viewer/NCViewerImage/NCViewerImageView.swift

@@ -0,0 +1,77 @@
+
+import UIKit
+
+public class NCViewerImageView: NCViewerImageNibLoadingView {
+    @IBOutlet weak var collectionView: UICollectionView!
+
+    public var assets: [NCViewerImageAsset?]? {
+        didSet {
+            self.collectionView.reloadData()
+        }
+    }
+
+    private var preselectedIndex: Int = -1
+
+    override public func willMove(toSuperview newSuperview: UIView?) {
+        super.willMove(toSuperview: newSuperview)
+        self.collectionView.register(UINib.init(nibName: String(describing: NCViewerImageCollectionViewCell.self), bundle: Bundle(for: type(of: self))), forCellWithReuseIdentifier: NCViewerImageCollectionViewCell.reusableIdentifier)
+    }
+
+    public override func layoutSubviews() {
+        super.layoutSubviews()
+        self.collectionView.collectionViewLayout.invalidateLayout()
+        if let indexPath = self.collectionView.indexPathsForVisibleItems.last {
+            DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
+                self.collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: false)
+            }
+        }
+    }
+
+    public func preselectItem(at index: Int) {
+        self.preselectedIndex = index
+    }
+
+    public override func draw(_ rect: CGRect) {
+        super.draw(rect)
+        if preselectedIndex != -1 {
+            self.collectionView.scrollToItem(at: IndexPath(row: self.preselectedIndex, section: 0), at: .centeredHorizontally, animated: false)
+            preselectedIndex = -1
+        }
+    }
+}
+
+extension NCViewerImageView: UICollectionViewDataSource {
+
+    public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        return assets?.count ?? 0
+    }
+
+    public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+        let cell: NCViewerImageCollectionViewCell = (collectionView.dequeueReusableCell(withReuseIdentifier: NCViewerImageCollectionViewCell.reusableIdentifier, for: indexPath) as? NCViewerImageCollectionViewCell)!
+        cell.withImageAsset(assets?[indexPath.row])
+        cell.delegate = self
+        return cell
+    }
+}
+
+extension NCViewerImageView: UICollectionViewDelegate {
+    public func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
+        (cell as? NCViewerImageCollectionViewCell)?.cancelPendingDataTask()
+    }
+
+    public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
+        (cell as? NCViewerImageCollectionViewCell)?.withImageAsset(assets?[indexPath.row])
+    }
+}
+
+extension NCViewerImageView: UICollectionViewDelegateFlowLayout {
+    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
+        return CGSize(width: floor(collectionView.frame.size.width), height: floor(collectionView.frame.size.height))
+    }
+}
+
+extension NCViewerImageView: NCViewerImageCollectionViewCellDelegate {
+    func didStartZooming(_ cell: NCViewerImageCollectionViewCell) {
+
+    }
+}

+ 47 - 0
iOSClient/Viewer/NCViewerImage/NCViewerImageView.xib

@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15705" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15706"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="NCViewerImageView" customModule="Nextcloud" customModuleProvider="target">
+            <connections>
+                <outlet property="collectionView" destination="dvH-x4-Vkp" id="CNC-Vg-HLc"/>
+            </connections>
+        </placeholder>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB">
+            <rect key="frame" x="0.0" y="0.0" width="375" height="612"/>
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+            <subviews>
+                <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" pagingEnabled="YES" dataMode="none" translatesAutoresizingMaskIntoConstraints="NO" id="dvH-x4-Vkp">
+                    <rect key="frame" x="0.0" y="0.0" width="375" height="612"/>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="0.0" minimumInteritemSpacing="0.0" id="vbZ-6V-yr3">
+                        <size key="itemSize" width="50" height="50"/>
+                        <size key="headerReferenceSize" width="0.0" height="0.0"/>
+                        <size key="footerReferenceSize" width="0.0" height="0.0"/>
+                        <inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
+                    </collectionViewFlowLayout>
+                    <connections>
+                        <outlet property="dataSource" destination="-1" id="cbr-e4-idB"/>
+                        <outlet property="delegate" destination="-1" id="e15-QR-GRy"/>
+                    </connections>
+                </collectionView>
+            </subviews>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstAttribute="trailing" secondItem="dvH-x4-Vkp" secondAttribute="trailing" id="8W6-pH-jYH"/>
+                <constraint firstAttribute="bottom" secondItem="dvH-x4-Vkp" secondAttribute="bottom" id="MUe-05-edB"/>
+                <constraint firstItem="dvH-x4-Vkp" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="iWm-ue-m5e"/>
+                <constraint firstItem="dvH-x4-Vkp" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="k3c-b7-PBB"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <point key="canvasLocation" x="-33" y="26"/>
+        </view>
+    </objects>
+</document>

+ 0 - 243
iOSClient/Viewer/NCViewerPhoto/NCViewerPhotoImageScrollView.swift

@@ -1,243 +0,0 @@
-import UIKit
-
-class NCViewerPhotoImageScrollView: UIScrollView, UIScrollViewDelegate {
-	
-	var index: Int!
-	var zoomView: UIImageView!
-	var tilingView: NCViewerPhotoTilingView?
-	
-	lazy var zoomingTap: UITapGestureRecognizer = {
-		let zoomingTap = UITapGestureRecognizer(target: self, action: #selector(handleZoomingTap(_:)))
-		zoomingTap.numberOfTapsRequired = 2
-		
-		return zoomingTap
-	}()
-
-	override init(frame: CGRect) {
-		super.init(frame: frame)
-		
-		self.showsHorizontalScrollIndicator = false
-		self.showsVerticalScrollIndicator = false
-        self.decelerationRate = UIScrollView.DecelerationRate.fast
-		self.delegate = self
-	}
-	
-	required init?(coder aDecoder: NSCoder) {
-		fatalError("init(coder:) has not been implemented")
-	}
-	
-	override func layoutSubviews() {
-		super.layoutSubviews()
-		self.centerImage()
-	}
-	
-	//MARK: - Configure scrollView to display new image
-	func display(_ image: UIImage) {
-		
-		//1. clear the previous image
-		zoomView?.removeFromSuperview()
-		zoomView = nil
-				
-		//2. make a new UIImageView for the new image
-		zoomView = UIImageView(image: image)
-		
-		self.addSubview(zoomView)
-		
-		self.configureFor(image.size)
-	}
-	
-	func displayTiledImage(in url: URL, size imageSize: CGSize) {
-		
-		// clear views for the previous image
-		zoomView?.removeFromSuperview()
-		zoomView = nil
-		tilingView = nil
-		
-		// make views to display the new image
-		zoomView = UIImageView(frame: CGRect(origin: CGPoint.zero, size: imageSize))
-		let image = placeholderImage(in: url)
-		zoomView.image = image
-		self.addSubview(zoomView)
-		
-		self.tilingView = NCViewerPhotoTilingView(in: url, size: imageSize)
-		self.zoomView?.addSubview(self.tilingView!)
-		
-		self.configureFor(imageSize)
-	}
-
-	
-	func configureFor(_ imageSize: CGSize) {
-		self.contentSize = imageSize
-		self.setMaxMinZoomScaleForCurrentBounds()
-		self.zoomScale = self.minimumZoomScale
-		
-		//Enable zoom tap
-		self.zoomView.addGestureRecognizer(self.zoomingTap)
-		self.zoomView.isUserInteractionEnabled = true
-	}
-	
-	func setMaxMinZoomScaleForCurrentBounds() {
-		let boundsSize = self.bounds.size
-		let imageSize = zoomView.bounds.size
-		
-		//1. calculate minimumZoomscale
-		let xScale =  boundsSize.width  / imageSize.width    // the scale needed to perfectly fit the image width-wise
-		let yScale = boundsSize.height / imageSize.height  // the scale needed to perfectly fit the image height-wise
-		
-		let minScale = min(xScale, yScale)                 // use minimum of these to allow the image to become fully visible
-		
-		//2. calculate maximumZoomscale
-		var maxScale: CGFloat = 1.0
-		
-		if minScale < 0.1 {
-			maxScale = 0.3
-		}
-		
-		if minScale >= 0.1 && minScale < 0.5 {
-			maxScale = 0.7
-		}
-		
-		if minScale >= 0.5 {
-			maxScale = max(1.0, minScale)
-		}
-		
-		
-		self.maximumZoomScale = maxScale
-		self.minimumZoomScale = minScale
-	}
-	
-	func centerImage() {
-		// center the zoom view as it becomes smaller than the size of the screen
-		let boundsSize = self.bounds.size
-		var frameToCenter = zoomView?.frame ?? CGRect.zero
-		
-		// center horizontally
-		if frameToCenter.size.width < boundsSize.width {
-			frameToCenter.origin.x = (boundsSize.width - frameToCenter.size.width)/2
-		}
-		else {
-			frameToCenter.origin.x = 0
-		}
-		
-		// center vertically
-		if frameToCenter.size.height < boundsSize.height {
-			frameToCenter.origin.y = (boundsSize.height - frameToCenter.size.height)/2
-		}
-		else {
-			frameToCenter.origin.y = 0
-		}
-		
-		zoomView?.frame = frameToCenter
-	}
-
-	//MARK: - UIScrollView Delegate Methods
-	
-	func viewForZooming(in scrollView: UIScrollView) -> UIView? {
-		return self.zoomView
-	}
-	
-	func scrollViewDidZoom(_ scrollView: UIScrollView) {
-		self.centerImage()
-	}
-	
-	//MARK: - Methods called during rotation to preserve the zoomScale and the visible portion of the image
-	
-	// returns the center point, in image coordinate space, to try restore after rotation.
-	func pointToCenterAfterRotation() -> CGPoint {
-		let boundsCenter = CGPoint(x: self.bounds.midX, y: self.bounds.midY)
-		return self.convert(boundsCenter, to: zoomView)
-	}
-	
-	// returns the zoom scale to attempt to restore after rotation.
-	func scaleToRestoreAfterRotation() -> CGFloat {
-		var contentScale = self.zoomScale
-		
-		// If we're at the minimum zoom scale, preserve that by returning 0, which will be converted to the minimum
-		// allowable scale when the scale is restored.
-		if contentScale <= self.minimumZoomScale + CGFloat.ulpOfOne {
-			contentScale = 0
-		}
-		
-		return contentScale
-	}
-	
-	func maximumContentOffset() -> CGPoint {
-		let contentSize = self.contentSize
-		let boundSize = self.bounds.size
-		return CGPoint(x: contentSize.width - boundSize.width, y: contentSize.height - boundSize.height)
-	}
-	
-	func minimumContentOffset() -> CGPoint {
-		
-		return CGPoint.zero
-	}
-	
-	func restoreCenterPoint(to oldCenter: CGPoint, oldScale: CGFloat) {
-		
-		// Step 1: restore zoom scale, first making sure it is within the allowable range.
-		self.zoomScale = min(self.maximumZoomScale, max(self.minimumZoomScale, oldScale))
-		
-		
-		// Step 2: restore center point, first making sure it is within the allowable range.
-		
-		// 2a: convert our desired center point back to our own coordinate space
-		let boundsCenter = self.convert(oldCenter, from: zoomView)
-		// 2b: calculate the content offset that would yield that center point
-		var offset = CGPoint(x: boundsCenter.x - self.bounds.size.width/2.0, y: boundsCenter.y - self.bounds.size.height/2.0)
-		// 2c: restore offset, adjusted to be within the allowable range
-		let maxOffset = self.maximumContentOffset()
-		let minOffset = self.minimumContentOffset()
-		offset.x = max(minOffset.x, min(maxOffset.x, offset.x))
-		offset.y = max(minOffset.y, min(maxOffset.y, offset.y))
-		self.contentOffset = offset
-	}
-
-	//MARK: - Handle ZoomTap
-	
-	@objc func handleZoomingTap(_ sender: UITapGestureRecognizer) {
-		let location = sender.location(in: sender.view)
-		self.zoom(to: location, animated: true)
-	}
-	
-	func zoom(to point: CGPoint, animated: Bool) {
-		let currentScale = self.zoomScale
-		let minScale = self.minimumZoomScale
-		let maxScale = self.maximumZoomScale
-		
-		if (minScale == maxScale && minScale > 1) {
-			return;
-		}
-		
-		let toScale = maxScale
-		let finalScale = (currentScale == minScale) ? toScale : minScale
-		let zoomRect = self.zoomRect(for: finalScale, withCenter: point)
-		self.zoom(to: zoomRect, animated: animated)
-	}
-	
-	
-	// The center should be in the imageView's coordinates
-	func zoomRect(for scale: CGFloat, withCenter center: CGPoint) -> CGRect {
-		var zoomRect = CGRect.zero
-		let bounds = self.bounds
-		
-		// the zoom rect is in the content view's coordinates.
-		//At a zoom scale of 1.0, it would be the size of the imageScrollView's bounds.
-		//As the zoom scale decreases, so more content is visible, the size of the rect grows.
-		zoomRect.size.width = bounds.size.width / scale
-		zoomRect.size.height = bounds.size.height / scale
-		
-		// choose an origin so as to get the right center.
-		zoomRect.origin.x = center.x - (zoomRect.size.width / 2.0)
-		zoomRect.origin.y = center.y - (zoomRect.size.height / 2.0)
-		
-		return zoomRect
-	}
-	
-	func placeholderImage(in url: URL) -> UIImage? {
-		let name = url.deletingPathExtension().lastPathComponent
-		let imageName = "\(name)_Placeholder.jpg"
-		let url = url.appendingPathComponent(imageName)
-		return UIImage(contentsOfFile: url.path)
-	}
-
-}

+ 0 - 758
iOSClient/Viewer/NCViewerPhoto/NCViewerPhotoTileManager.swift

@@ -1,758 +0,0 @@
-
-import Foundation
-import UIKit
-
-public struct NCViewerPhotoTileManager {
-	
-	private enum TileMakerError: Error {
-		case destinationContextFailedToMakeImage
-		case inputImageNotFound
-		case failedToCreateTheOutputBitmapContext
-	}
-	
-	private let destImageSizeMB: Int // The resulting image will be (x)MB of uncompressed image data.
-	private let sourceImageTileSizeMB: Int // The tile size will be (x)MB of uncompressed image data.
-	private let tileSize: Int
-	
-	/* Constants for all other iOS devices are left to be defined by the developer.
-	The purpose of this sample is to illustrate that device specific constants can
-	and should be created by you the developer, versus iterating a complete list. */
-	
-	private let bytesPerMB: Int = 1048576
-	private let bytesPerPixel: Int = 4
-	
-	
-	private var pixelsPerMB: Int {
-		return ( bytesPerMB / bytesPerPixel ) // 262144 pixels, for 4 bytes per pixel.
-	}
-	
-	private var destTotalPixels: Int {
-		return destImageSizeMB * pixelsPerMB
-	}
-	
-	private var tileTotalPixels: Int {
-		return sourceImageTileSizeMB * pixelsPerMB
-	}
-	
-	private let destSeamOverlap: Float = 2 // the numbers of pixels to overlap the seams where tiles meet.
-	
-	private let fileManager = FileManager.default
-	
-	/**
-		A Boolean value that controls whether the sourceimage will be down sized or not.
-	
-		If the value of this property is true, the source image be down sized.
-		The default value is true.
-	*/
-	public  var downSizeSourceImage: Bool = true
-
-	
-	/**
-	Initializes and returns a newly struct with the specified parameters.
-	The methods of this struct uses to manage tiles.
-	
-	 - Parameters:
-	   - destImageSize:
-			The maximum size of destination image in MB when uncomperessed in memory.
-			The value should be smaller than uncompressed size of source image in memory.
-			If you set a value bigger than the original size of source image for this parameter,
-			the original size of image uses for tiling. To know how is the size of source image
-			when uncomperessed in memory use **totalMBForImage(in url: URL)** method.
-			The default value of this parameter is 60.
-	
-	   - sourceImageDownSizingTileSize:
-			The size of tiles for down sizing the source image in MB,  if you want to down size of source image.
-			This argument is  because of that, we do not want to down size whole of source image instantly,
-			because that needs to load whole of source image in memory and it occupies a lot of memory.
-			Instead we shrink the source image to some small tiles and down size these tiles in order.
-			You should be careful about setting value of this parameter. Setting very small value causes high cpu
-			usage and setting very large value causes high memory usage. The default value of this parameter is 20.
-	
-	   - tileSize:
-			The size of each tile used for CATiledLayer. The default value is 256.
-	
-	- Returns:
-		An initialized struct.
-	*/
-
-	public init(destImageSize: Int = 60, sourceImageDownSizingTileSize: Int = 20, tileSize: Int = 256) {
-		self.destImageSizeMB = destImageSize
-		self.sourceImageTileSizeMB = sourceImageDownSizingTileSize
-		self.tileSize = tileSize
-	}
-	
-	/**
-	A method for getting the url of tiles for each tiled image.
-	This method returns a directory url.
-	- Parameter imageName: name of image that needs its tiles url
-	- Returns: url of tiles respect to name of image passed.
-	*/
-	public func urlOfTiledImage(named imageName: String) -> URL {
-		
-		let destinationURL = fileManager.temporaryDirectory.appendingPathComponent("TileManager", isDirectory: true).appendingPathComponent(imageName, isDirectory: true)
-		if !fileManager.fileExists(atPath: destinationURL.path) {
-			
-			do {
-				
-				try fileManager.createDirectory(at: destinationURL, withIntermediateDirectories: true, attributes: nil)
-				
-			}
-			catch let error {
-				fatalError("cant create directory at \(destinationURL), cause error: \(error)")
-			}
-			
-		}
-		
-		return destinationURL
-	}
-	
-	/**
-	A method for getting placeholder image of each tiled image.
-	This placeholder is created the first time the tiles of each image being created.
-	- Parameter imageName: name of image that needs its placeholder image url
-	- Returns: url of placeholder image respect to name of image passed.
-	*/
-	public func urlOfPlaceholderOfImage(named imageName: String) -> URL? {
-		let directoryURL = urlOfTiledImage(named: imageName)
-		let imageName = "\(imageName)_Placeholder.jpg"
-		let url = directoryURL.appendingPathComponent(imageName)
-		if fileManager.fileExists(atPath: url.path) {
-			return url
-		}
-		return nil
-	}
-	
-	/**
-	Removes directory of tiles respect to each tiled image if exist.
-	- Parameter imageName: name of image that needs to remove its tiles.
-	*/
-	public func removeTilesForImage(named imageName: String) {
-		let url = urlOfTiledImage(named: imageName)
-		do {
-			try self.fileManager.removeItem(at: url)
-		}
-		catch {
-			print(error)
-		}
-	}
-	
-	/**
-	Removes directory of whole tiles that created for this app.
-	*/
-	public func clearCache() {
-		let tileManagerURL = fileManager.temporaryDirectory.appendingPathComponent("TileManager", isDirectory: true)
-		do {
-			try self.fileManager.removeItem(at: tileManagerURL)
-		}
-		catch {
-			print(error)
-		}
-	}
-	
-	/**
-	Checks whether it is needed to make tiles for the image that passed its url.
-	This method compares resolution of passed url's image with phone screen resolution
-	- Parameter url: The url of image that want to check its need to tiling.
-	- Returns: Returns true if image resolution is bigger than phone screen resolution otherwise
-			   returns false
-	*/
-	public func needsTilingImage(in url: URL) -> Bool {
-		do {
-			
-			let sourceResolution = try resolutionForImage(in: url)
-			
-			let sourceMaximumEdge: CGFloat = sourceResolution.width > sourceResolution.height ? sourceResolution.width : sourceResolution.height
-			
-			let screenTotalSize = UIScreen.main.bounds.size
-			let screenScale = UIScreen.main.scale
-			
-			let screenMinimumEdge: CGFloat = screenTotalSize.width < screenTotalSize.height ? screenTotalSize.width : screenTotalSize.height
-			
-			return sourceMaximumEdge > screenMinimumEdge*screenScale
-			
-		} catch {
-			print(error)
-		}
-		
-		return false
-	}
-
-	/**
-	Checks whether tiles made for the image that passed its url.
-	- Parameter imageName: name of image that needs to check.
-	- Returns: Returns true if tiles are exist for image that passed its url.
-	*/
-	public func tilesMadeForImage(named imageName: String) -> Bool {
-		 return urlOfImageInfoForImage(named: imageName) != nil
-	}
-	
-	/**
-	- Parameter imageName: name of image that needs its size.
-	- Returns: Returns the resolution size of image that its tiles are made. This value is saved in a **plist** file next to the tiles.
-	*/
-	public func sizeOfTiledImage(named imageName: String) -> CGSize? {
-		if let url = urlOfImageInfoForImage(named: imageName) {
-			let plist = NSArray(contentsOf: url)
-			if let dic = plist!.firstObject as? [String: Any] {
-				let width = dic["width"] as? CGFloat ?? 0
-				let height = dic["height"] as? CGFloat ?? 0
-				let size = CGSize(width: width, height: height)
-				return size
-			}
-		}
-		return nil
-	}
-	
-	
-	/**
-	- Parameter url: url of image that needs its resolution size.
-	- Returns: Returns the resolution size of image that passed its url.
-	*/
-	public func resolutionForImage(in url: URL) throws -> CGSize {
-		// create an image from the image filename constant. Note this
-		//  doesn't actually read any pixel information from disk, as that
-		// is actually done at draw time.
-		let path = url.path
-		
-		// The input image file
-		var sourceImage: UIImage?
-		sourceImage = UIImage(contentsOfFile: path)
-		guard  sourceImage != nil else {
-			throw TileMakerError.inputImageNotFound
-		}
-		
-		// get the width and height of the input image using
-		// core graphics image helper functions.
-		let sourceResolution = CGSize(width: CGFloat(sourceImage!.cgImage!.width), height: CGFloat(sourceImage!.cgImage!.height))
-		
-		return sourceResolution
-	}
-	
-	
-	/**
-	This method calculate that how would be the resolution of image that passed its url if it being down sized with the parameter of initializer.
-	- Parameter url: url of image that needs its resolution size.
-	- Returns: Returns the destination resolution size of image that passed its url.
-	*/
-	public func destinationResolutionForImage(in url: URL) throws -> CGSize {
-		do {
-			// get the width and height of the input image using
-			// core graphics image helper functions.
-			let sourceResolution = try self.resolutionForImage(in: url)
-			
-			// use the width and height to calculate the total number of pixels
-			// in the input image.
-			let sourceTotalPixels = sourceResolution.width * sourceResolution.height
-			
-			// determine the scale ratio to apply to the input image
-			// that results in an output image of the defined size.
-			// see destImageSizeMB, and how it relates to destTotalPixels.
-			
-			let imageScale: CGFloat = self.downSizeSourceImage ? CGFloat(self.destTotalPixels) / sourceTotalPixels : 1.0
-			
-			// use the image scale to calcualte the output image width, height
-			let destResolution = CGSize(width: sourceResolution.width * imageScale, height: sourceResolution.height * imageScale)
-			
-			return destResolution
-			
-		} catch let error {
-			
-			throw error
-		}
-	}
-	
-	/**
-	This method calculate that total size (in megabyte) of image that passed its url when it is uncompressed and loaded in memory.
-	- Parameter url: url of image that needs its total megabyte size in memory.
-	- Returns: Returns total megabyte size of image in memory.
-	*/
-	public func totalMBForImage(in url: URL) throws -> CGFloat {
-		do {
-			
-			// get the width and height of the input image using
-			// core graphics image helper functions.
-			let sourceResolution = try self.resolutionForImage(in: url)
-			
-			// use the width and height to calculate the total number of pixels
-			// in the input image.
-			let sourceTotalPixels = sourceResolution.width * sourceResolution.height
-			
-			// calculate the number of MB that would be required to store
-			// this image uncompressed in memory.
-			let sourceTotalMB = sourceTotalPixels / CGFloat(self.pixelsPerMB)
-			
-			return sourceTotalMB
-			
-		} catch let error {
-			
-			throw error
-		}
-	}
-	
-    /**
-	Down sizes, makes placeholder and Tiles for given image url.
-	
-	- Parameters:
-		- url: url of image that needs to make tiles for it
-		- placeholderCompletion:
-			A block to be executed when the making of placeholder ends. This block has no return value and takes url argument of created placeholder image and error argument for creating placholder. url may be nil if an error occurs about making placeholder. Error will be nil if no error occurs.
-	
-		- tilingCompletion:
-			A block to be executed when the making of tiles ends. This block has no return value and takes
-			three argument. An String and CGSize as name and size of tiled image, an error if some errors happened.
-			If an error occurs, String and CGSize arguments may be nil. If no error occurs, Error will be nil.
-	*/
-	public func makeTiledImage(for url: URL, placeholderCompletion: @escaping (URL?, Error?) -> Swift.Void, tilingCompletion: @escaping (String?, CGSize?, Error?) -> Swift.Void) {
-		// create an image from the image filename constant. Note this
-		// doesn't actually read any pixel information from disk, as that
-		// is actually done at draw time.
-		
-		// The input image file
-		guard let sourceImage = UIImage(contentsOfFile: url.path) else {
-			print("error: input image not found!")
-			DispatchQueue.main.async {
-				tilingCompletion(nil, nil, TileMakerError.inputImageNotFound)
-			}
-			return
-		}
-		
-		let imageNamePrefix = url.deletingPathExtension().lastPathComponent
-		
-		let destinationURL = self.urlOfTiledImage(named: imageNamePrefix)
-		
-		self.makePlaceholder(for: sourceImage.cgImage!, to: destinationURL, usingPrefix: imageNamePrefix) { (url, error) in
-			if error != nil {
-				DispatchQueue.main.async {
-					tilingCompletion(nil, nil, error)
-				}
-				return
-			}
-			else {
-				DispatchQueue.main.async {
-					placeholderCompletion(url, error)
-				}
-			}
-		}
-
-		self.downSize(sourceImage, completion: { (image, error) in
-			guard error == nil else {
-				DispatchQueue.main.async {
-					tilingCompletion(nil, nil, error)
-				}
-				return
-			}
-			
-			self.makeTiles(for: image!, to: destinationURL, usingPrefix: imageNamePrefix, tilingCompletion: { (imageName, imageSize, error) in
-				
-				DispatchQueue.main.async {
-					tilingCompletion(imageName, imageSize, error)
-				}
-			})
-			
-		})
-		
-	}
-	
-	/**
-	A method for getting url of **imageInfo.plist** that contains name, width and height of each tiled image.
-	This file is created the first time the tiles of each image being created.
-	- Parameter imageName: name of image that needs its imageInfo.plist file url
-	- Returns: url of imageInfo.plist respect to name of image passed.
-	*/
-	private func urlOfImageInfoForImage(named imageName: String) -> URL? {
-		let directoryURL = urlOfTiledImage(named: imageName)
-		print(directoryURL.path)
-		
-		let url = directoryURL.appendingPathComponent("imageInfo.plist")
-		return fileManager.fileExists(atPath: url.path) ? url : nil
-	}
-	
-	/**
-	Down size given image to an image with the size of megabyte that specified in initializer.
-	
-	- Parameters:
-	 	- sourceImage: The imgae want to downsize it
-	 	- completion:  A block to be executed when the down sizing ends. I takes two argument. the downsized image as CGImage and error. If an error occurs the CGImage may be nil. if no error occurs, Error will be nil.
-	*/
-	private func downSize(_ sourceImage: UIImage, completion: @escaping (CGImage?, Error?) -> ()) {
-		
-		/* the temporary container used to hold the resulting output image pixel
-		data, as it is being assembled. */
-		var destContext: CGContext!
-		
-		
-		DispatchQueue.global().async {
-			
-			autoreleasepool {
-				
-//				guard let sourceImage = UIImage(contentsOfFile: path) else {
-//					print("error: input image not found!")
-//					completion(nil, TileMakerError.inputImageNotFound)
-//					return
-//				}
-				
-				// get the width and height of the input image using
-				// core graphics image helper functions.
-				let sourceResolution = CGSize(width: CGFloat(sourceImage.cgImage!.width), height: CGFloat(sourceImage.cgImage!.height))
-				
-				// use the width and height to calculate the total number of pixels
-				// in the input image.
-				let sourceTotalPixels = sourceResolution.width * sourceResolution.height
-				
-				// calculate the number of MB that would be required to store
-				// this image uncompressed in memory.
-				let sourceTotalMB = sourceTotalPixels / CGFloat(self.pixelsPerMB)
-
-				// determine the scale ratio to apply to the input image
-				// that results in an output image of the defined size.
-				// see destImageSizeMB, and how it relates to destTotalPixels.
-				var imageScale: CGFloat = self.downSizeSourceImage ? CGFloat(self.destTotalPixels) / sourceTotalPixels : 1.0
-				
-				if Int(sourceTotalMB) <= self.destImageSizeMB {
-					imageScale = 1.0
-				}
-				
-				// use the image scale to calcualte the output image width, height
-				let destResolution = CGSize(width: sourceResolution.width * imageScale, height: sourceResolution.height * imageScale)
-				
-				// create an offscreen bitmap context that will hold the output image
-				// pixel data, as it becomes available by the downscaling routine.
-				// use the RGB colorspace as this is the colorspace iOS GPU is optimized for.
-				let colorSpace = CGColorSpaceCreateDeviceRGB()
-				let bytesPerRow = self.bytesPerPixel * Int(destResolution.width)
-				
-				
-				// create the output bitmap context
-				destContext = CGContext(data: nil, width: Int(destResolution.width), height: Int(destResolution.height), bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)
-				
-				// remember CFTypes assign/check for NULL. NSObjects assign/check for nil.
-				if destContext == nil {
-					completion(nil, TileMakerError.failedToCreateTheOutputBitmapContext)
-					print("error: failed to create the output bitmap context!")
-					return
-				}
-				
-				// flip the output graphics context so that it aligns with the
-				// cocoa style orientation of the input document. this is needed
-				// because we used cocoa's UIImage -imageNamed to open the input file.
-				destContext.translateBy(x: 0, y: destResolution.height)
-				destContext.scaleBy(x: 1, y: -1)
-				
-				// now define the size of the rectangle to be used for the
-				// incremental blits from the input image to the output image.
-				// we use a source tile width equal to the width of the source
-				// image due to the way that iOS retrieves image data from disk.
-				// iOS must decode an image from disk in full width 'bands', even
-				// if current graphics context is clipped to a subrect within that
-				// band. Therefore we fully utilize all of the pixel data that results
-				// from a decoding opertion by anchoring our tile size to the full
-				// width of the input image.
-				var sourceTile = CGRect.zero
-				sourceTile.size.width = sourceResolution.width
-				
-				// the source tile height is dynamic. Since we specified the size
-				// of the source tile in MB, see how many rows of pixels height
-				// can be given the input image width.
-				sourceTile.size.height = floor(CGFloat(self.tileTotalPixels) / sourceTile.size.width)
-				print("source tile size: \(sourceTile.size.width) x \(sourceTile.size.height)")
-				//			sourceTile.origin.x = 0.0
-				
-				// the output tile is the same proportions as the input tile, but
-				// scaled to image scale.
-				var destTile = CGRect.zero
-				destTile.size.width = destResolution.width
-				destTile.size.height = sourceTile.size.height * imageScale
-				//			destTile.origin.x = 0.0
-				
-				print("source tile size: \(sourceTile.size.width) x \(sourceTile.size.height)")
-				
-				// the SeamOverlap is the number of pixels to overlap tiles as they are assembled.
-				// the source seam overlap is proportionate to the destination seam overlap.
-				// this is the amount of pixels to overlap each tile as we assemble the ouput image.
-				let sourceSeamOverlap = floor((CGFloat(self.destSeamOverlap) / destResolution.height) * sourceResolution.height)
-				print("dest seam overlap: \(self.destSeamOverlap), source seam overlap: \(sourceSeamOverlap)")
-				
-				var sourceTileImage: CGImage!
-				
-				// calculate the number of read/write opertions required to assemble the
-				// output image.
-				var iterations = Int(sourceResolution.height / sourceTile.height)
-				
-				// if tile height doesn't divide the image height evenly, add another iteration
-				// to account for the remaining pixels.
-				let remainder = Int(sourceResolution.height.truncatingRemainder(dividingBy: sourceTile.size.height))
-				if remainder != 0 {
-					iterations += 1
-				}
-				
-				// add seam overlaps to the tiles, but save the original tile height for y coordinate calculations.
-				let sourceTileHeightMinusOverlap = sourceTile.size.height
-				sourceTile.size.height += sourceSeamOverlap
-				destTile.size.height += CGFloat(self.destSeamOverlap)
-				
-				//				print("beginning downsize. iterations: \(iterations), tile height: \(sourceTile.size.height), remainder height: \(remainder)")
-				
-				for y in 0..<iterations {
-					
-					// create an autorelease pool to catch calls to -autorelease made within the downsize loop.
-					autoreleasepool {
-						
-						//						print("iteration \(y+1) of \(iterations)")
-						
-						sourceTile.origin.y = CGFloat(y) * sourceTileHeightMinusOverlap + CGFloat(sourceSeamOverlap)
-						destTile.origin.y = (destResolution.height ) - ( ( CGFloat(y) + 1 ) * sourceTileHeightMinusOverlap * imageScale + CGFloat(self.destSeamOverlap))
-						
-						// create a reference to the source image with its context clipped to the argument rect.
-						sourceTileImage = sourceImage.cgImage?.cropping(to: sourceTile)
-						
-						// if this is the last tile, it's size may be smaller than the source tile height.
-						// adjust the dest tile size to account for that difference.
-						if y == iterations - 1 && remainder != 0 {
-							var dify = destTile.size.height
-							destTile.size.height = CGFloat(sourceTileImage.height) * imageScale
-							dify -= destTile.size.height
-							destTile.origin.y += dify
-						}
-						
-						// read and write a tile sized portion of pixels from the input image to the output image.
-						destContext.draw(sourceTileImage, in: destTile)
-					}
-				}
-				
-				//				print("downsize complete.")
-				
-				if let image = destContext.makeImage() {
-					completion(image, nil)
-				}
-				else {
-					completion(nil, TileMakerError.destinationContextFailedToMakeImage)
-				}
-				
-			}
-		}
-	}
-	
-	
-	/**
-	  Make tiles in 4 diferent scale for given image and save tiles in given directory url. The scales are 0.125, 0.25, 0.5, 1.0 .
-	
-	  - Parameters:
-		   - image: image that wants make tiles for it
-		   - directoryURL: destination url want tiles save there.
-		   - prefix: The name that uses for naming tiles of image.
-		   - tilingCompletion:
-				A block to be executed when the making of tiles ends. This block has no return value and takes
-				three argument. An String and CGSize as name and size of tiled image, an error if some errors happened.
-				If an error occurs, String and CGSize arguments may be nil. If no error occurs, Error will be nil.
-	*/
-	private func makeTiles(for image: CGImage, to directoryURL: URL, usingPrefix prefix: String, tilingCompletion: @escaping (String?, CGSize?, Error?) -> ()) {
-		DispatchQueue.global().async {
-			
-			var scale: CGFloat = 0.125
-			var iterations: Int = 4
-			let imageMaxEdge = image.width > image.height ? image.width : image.height
-			let screenSize = CGSize(width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height)
-			let screenMinEdge = screenSize.width > screenSize.height ? screenSize.width : screenSize.height
-			let screenScale = UIScreen.main.scale
-			let ratio = (screenMinEdge*screenScale*0.2)/CGFloat(imageMaxEdge)
-			
-			if ratio > 0.125, ratio <= 0.25 {
-				scale = 0.25
-				iterations = 3
-			}
-			if ratio > 0.25, ratio <= 0.5 {
-				scale = 0.5
-				iterations = 2
-			}
-			if ratio > 0.5 {
-				scale = 1
-				iterations = 1
-			}
-			
-			
-			DispatchQueue.concurrentPerform(iterations: iterations, execute: { (count) in
-				print("scale is", scale*pow(2, CGFloat(count)))
-				self.makeTiles(for: image, inScale: scale*pow(2, CGFloat(count)), to: directoryURL, usingPrefix: prefix) { (error) in
-					if error != nil {
-						tilingCompletion(nil, nil, error)
-						return
-					}
-				}
-				
-			})
-			
-			let imageWidth = CGFloat(image.width)
-			let imageHeight = CGFloat(image.height)
-			let imageSize = CGSize(width: imageWidth, height: imageHeight)
-			self.add(imageName: prefix, with: imageSize, toPropertyListAt: directoryURL) { (error) in
-				if error != nil {
-					tilingCompletion(nil, nil, error)
-				}
-				else {
-					tilingCompletion(prefix, imageSize, nil)
-					
-				}
-			}
-		}
-	}
-	
-	/**
-	Make tiles in 4 diferent scale for given image and save tiles in given directory url. The scales are 0.125, 0.25, 0.5, 1.0 .
-	
-	- Parameters:
-		- imageName: Name of image that wants to save its information in its info propertylist
-		- size: size of image that wants to save its information in its info propertylist
-		- propertyListURL: destination url want tiles save there.
-		- completion:
-		A block to be executed when saving information to propertyList ends. This block has no return value and takes
-		one argument. An error if some errors happened. If no error occurs, Error will be nil.
-	*/
-	private func add(imageName:String, with size: CGSize, toPropertyListAt propertyListURL: URL, completion: (Error?) -> ()) {
-		let dic: [String: Any] = ["name": imageName, "width": size.width, "height": size.height]
-		
-		let fileManager = FileManager.default
-		let url = propertyListURL.appendingPathComponent("imageInfo.plist")
-		
-        if fileManager.fileExists(atPath: url.path) {
-            let plistArray = NSMutableArray(contentsOf: url)
-            plistArray?.add(dic)
-            plistArray?.write(to: url, atomically: true)
-            completion(nil)
-        } else {
-            NSArray(array: [dic]).write(to: url, atomically: true)
-            completion(nil)
-        }
-	}
-	
-	/**
-	Make placeholder for given image and save it in given directory url.
-	
-	- Parameters:
-		- image: image that wants make placeholder for it
-		- directoryURL: destination url want placeholder save there.
-		- prefix: The name that uses for naming placeholder of image.
-		- completion:
-		A block to be executed when the making of tiles ends. This block has no return value and takes
-		two argument. A URL and Error as url of placeholder, and error if some errors happened.
-		If an error occurs, url may be nil. If no error occurs, Error will be nil.
-	*/
-	private func makePlaceholder(for image: CGImage, to directoryURL: URL, usingPrefix prefix: String, completion: @escaping (URL?, Error?) -> ()) {
-		let imageWidth = CGFloat(image.width)
-		let imageHeight = CGFloat(image.height)
-		
-		let scale = UIScreen.main.bounds.width/imageWidth
-		let imageRect = CGRect(origin: .zero, size: CGSize(width: imageWidth*scale, height: imageHeight*scale))
-		
-		DispatchQueue.global().async {
-
-			UIGraphicsBeginImageContext(imageRect.size)
-			let context = UIGraphicsGetCurrentContext()
-			
-			context?.saveGState()
-			context?.translateBy(x: 0, y: imageRect.size.height)
-			context?.scaleBy(x: 1, y: -1)
-			
-			context?.draw(image, in: imageRect)
-			context?.restoreGState()
-			let lowQImage = context?.makeImage()
-			UIGraphicsEndImageContext()
-            let imageData = UIImage(cgImage: lowQImage!).pngData()
-			
-			let imageName = "\(prefix)_Placeholder.jpg"
-			let url = directoryURL.appendingPathComponent(imageName)
-			do {
-				try imageData!.write(to: url)
-			}
-			catch let error {
-				completion(nil, error)
-			}
-			completion(url, nil)
-		}
-	}
-	
-	/**
-	Make tiles in given scale for given image and save tiles in given directory url.
-	
-	- Parameters:
-		- size: Size that wants make tiles in that size. Default is nil and uses the size specified with initializer
-		- image: Image that wants make tiles for it.
-		- scale: Scale that wants make tiles for that scale.
-		- directoryURL: destination url want tiles save there.
-		- prefix: The name that uses for naming tiles of image.
-		- completion:
-		A block to be executed when saving information to propertyList ends. This block has no return value and takes
-		one argument. An error if some errors happened. If no error occurs, Error will be nil.
-	*/
-	private func makeTiles( in size: CGSize? = nil, for image: CGImage, inScale scale: CGFloat, to directoryURL: URL, usingPrefix prefix: String, completion: (Error?) -> ()) {
-		let size = size ?? CGSize(width: self.tileSize, height: self.tileSize)
-		
-		var image: CGImage! = image
-		
-		let imageWidth = CGFloat(image.width)
-		let imageHeight = CGFloat(image.height)
-		
-		let imageRect = CGRect(origin: .zero, size: CGSize(width: imageWidth*scale, height: imageHeight*scale))
-		var context: CGContext!
-		if scale != 2 {
-			UIGraphicsBeginImageContext(imageRect.size)
-			context = UIGraphicsGetCurrentContext()
-			
-			context?.saveGState()
-			
-			context?.draw(image!, in: imageRect)
-			context?.restoreGState()
-			image = context.makeImage()
-			UIGraphicsEndImageContext()
-		}
-		
-		let cols = imageRect.width/size.width
-		let rows = imageRect.height/size.height
-		
-		var fullColomns = floor(cols)
-		var fullRows = floor(rows)
-		
-		let remainderWidth = imageRect.width - fullColomns*size.width
-		let remainderHeight = imageRect.height - fullRows*size.height
-		
-		if cols > fullColomns { fullColomns += 1 }
-		if rows > fullRows { fullRows += 1 }
-		
-		let fullImage = image!
-		
-		for row in 0..<Int(fullRows) {
-			for col in 0..<Int(fullColomns ){
-				var tileSize = size
-				if col + 1 == Int(fullColomns) && remainderWidth > 0 {
-					// Last Column
-					tileSize.width = remainderWidth
-				}
-				if row + 1 == Int(fullRows) && remainderHeight > 0 {
-					// Last Row
-					tileSize.height = remainderHeight
-				}
-				
-				autoreleasepool {
-					
-					let tileImage = fullImage.cropping(to: CGRect(origin: CGPoint(x: CGFloat(col)*size.width, y: CGFloat(row)*size.height), size: tileSize))!
-                    let imageData = UIImage(cgImage: tileImage).pngData()
-					
-					let tileName = "\(prefix)_\(Int(scale*1000))_\(col)_\(row).png"
-					let url = directoryURL.appendingPathComponent(tileName)
-					do {
-						try imageData!.write(to: url)
-					}
-					catch {
-						print(error)
-						completion(error)
-						return
-					}
-					
-				}
-				
-			}
-		}
-		context = nil
-		completion(nil)
-	}
-
-}

+ 0 - 107
iOSClient/Viewer/NCViewerPhoto/NCViewerPhotoTilingView.swift

@@ -1,107 +0,0 @@
-import UIKit
-
-class NCViewerPhotoTilingView: UIView {
-	
-	var imageName: String
-	var url: URL
-	var tilingView: NCViewerPhotoTilingView?
-
-	// We use these two properties to avoid accessing tiledLayer from bg thread:
-	var storedTileSize: CGSize!
-	var storedBounds: CGRect!
-	
-	override class var layerClass: AnyClass {
-		return CATiledLayer.self
-	}
-
-	// returns layer property as CATiledLayer
-	var tiledLayer: CATiledLayer {
-		return self.layer as! CATiledLayer
-	}
-
-	/*
-	Force contentScaleFactor of 1, even on retina displays For the CATiledLayer
-	to handle the interaction between CATiledLayer and high resolution screens, we need to manually set the
-	tiling view's contentScaleFactor to 1.0. (If we omitted this, it would be 2.0 on high resolution screens,
-	which would cause the CATiledLayer to ask us for tiles of the wrong scales.)
-	*/
-	override var contentScaleFactor: CGFloat {
-		didSet {
-			super.contentScaleFactor = 1
-		}
-	}
-	
-	init(in url: URL, size: CGSize) {
-		self.url = url
-		self.imageName = url.deletingPathExtension().lastPathComponent
-		
-		super.init(frame: CGRect(x: 0, y: 0, width: size.width, height: size.height))
-		tiledLayer.levelsOfDetail = 4
-		
-		storedTileSize = tiledLayer.tileSize
-		storedBounds = self.bounds
-	}
-	
-	required init?(coder aDecoder: NSCoder) {
-		fatalError("init(coder:) has not been implemented")
-	}
-	
-    // Only override draw() if you perform custom drawing.
-    // An empty implementation adversely affects performance during animation.
-    override func draw(_ rect: CGRect) {
-		
-		let context = UIGraphicsGetCurrentContext()!
-		// get the scale from the context by getting the current transform matrix, then asking for
-		// its "a" component, which is one of the two scale components. We need to also ask for the "d" component as it might not be precisely the same as the "a" component, even at the "same" scale.
-		let scaleX: CGFloat = context.ctm.a
-		let scaleY: CGFloat = context.ctm.d
-
-		var tileSize = self.storedTileSize!
-		
-		// Even at scales lower than 100%, we are drawing into a rect in the coordinate system of the full
-		// image. One tile at 50% covers the width (in original image coordinates) of two tiles at 100%.
-		// So at 50% we need to stretch our tiles to double the width and height; at 25% we need to stretch
-		// them to quadruple the width and height; and so on.
-		// (Note that this means that we are drawing very blurry images as the scale gets low. At 12.5%,
-		// our lowest scale, we are stretching about 6 small tiles to fill the entire original image area.
-		// But this is okay, because the big blurry image we're drawing here will be scaled way down before
-		// it is displayed.)
-		
-		tileSize.width /= scaleX
-		tileSize.height /= -scaleY
-
-		// calculate the rows and columns of tiles that intersect the rect we have been asked to draw
-		let firstCol: Int = Int(floor(rect.minX/tileSize.width))
-		let lastCol: Int = Int(floor((rect.maxX-1)/tileSize.width))
-		let firstRow: Int = Int(floor(rect.minY/tileSize.height))
-		let lastRow: Int = Int(floor((rect.maxY-1)/tileSize.height))
-
-		for row in firstRow...lastRow {
-			for col in firstCol...lastCol {
-				guard let tile = tileFor(scale: scaleX, row: row, col: col) else {
-					return
-				}
-				var tileRect = CGRect(x: tileSize.width*CGFloat(col), y: tileSize.height*CGFloat(row), width: tileSize.width, height: tileSize.height)
-				
-				// if the tile would stick outside of our bounds, we need to truncate it so as
-				// to avoid stretching out the partial tiles at the right and bottom edges
-				tileRect = self.storedBounds.intersection(tileRect)
-				tile.draw(in: tileRect)
-			}
-		}
-
-    }
-	
-	func tileFor(scale: CGFloat, row: Int, col: Int) -> UIImage? {
-		//this accounts for a bug somewhere upstream that returns the scale as a floating point number just below the required value: 0.249... instead of 0.2500
-		let scale = scale < 1.0 ? Int(1/CGFloat(Int(1/scale))*1000) : Int(scale*1000)
-		
-		// we use "UIImage(contentsOfFile:)" instead of "UIImage(named:)" here because we don't
-		// want UIImage to cache our tiles
-		let tileName = "\(self.imageName)_\(scale)_\(col)_\(row).png"
-		
-		let path = url.appendingPathComponent(tileName).path
-		let image = UIImage(contentsOfFile: path)
-		return image
-	}
-}

+ 0 - 357
iOSClient/Viewer/NCViewerPhoto/NCViewerPhotoViewController.swift

@@ -1,357 +0,0 @@
-import UIKit
-
-class NCViewerPhotoViewController: UIViewController, UIScrollViewDelegate {
-	
-	let pagePadding: CGFloat = 10
-	var pagingScrollView: UIScrollView!
-	var recycledPages: Set<NCViewerPhotoImageScrollView> = []
-	var visiblePages: Set<NCViewerPhotoImageScrollView> = []
-	var firstVisiblePageIndexBeforeRotation: Int!
-	var singleTap: UITapGestureRecognizer!
-	var inTilingProcess: Set<String> = []
-	var currentImageName: String = ""
-    
-    var metadata: tableMetadata?
-
-	override func viewDidLoad() {
-		super.viewDidLoad()
-		
-		// single tap to show or hide navigation bar
-		self.singleTap = UITapGestureRecognizer(target: self, action: #selector(handleSingleTap))
-		self.view.addGestureRecognizer(self.singleTap)
-		
-		self.pagingScrollView = UIScrollView(frame: self.frameForPagingScrollView())
-		//self.updateBackgroundColor()
-		self.pagingScrollView.showsVerticalScrollIndicator = false
-		self.pagingScrollView.showsHorizontalScrollIndicator = false
-		self.pagingScrollView.isPagingEnabled = true
-		self.pagingScrollView.contentSize = self.contentSizeForPagingScrollView()
-		self.pagingScrollView.delegate = self
-        if #available(iOS 11.0, *) {
-            pagingScrollView.contentInsetAdjustmentBehavior = .never
-        } else {
-            automaticallyAdjustsScrollViewInsets = false
-        }
-		self.view.addSubview(self.pagingScrollView)
-		self.layoutPagingScrollView()
-		
-		self.tilePages()
-	}
-	
-	//MARK: - Tiling and page configuration
-	
-	func tilePages() {
-		// Calculate which pages should now be visible
-		let visibleBounds = pagingScrollView.bounds
-		
-		var firstNeededPageIndex: Int = Int(floor(visibleBounds.minX/visibleBounds.width))
-		var lastNeededPageIndex: Int = Int(floor((visibleBounds.maxX - 1)/visibleBounds.width))
-		firstNeededPageIndex = max(firstNeededPageIndex, 0)
-		lastNeededPageIndex = min(lastNeededPageIndex, self.imageCount - 1)
-		
-		//Recycle no longer needs pages
-		for page in self.visiblePages {
-			if page.index < firstNeededPageIndex || page.index > lastNeededPageIndex {
-				self.recycledPages.insert(page)
-				page.removeFromSuperview()
-			}
-		}
-		self.visiblePages.subtract(self.recycledPages)
-		
-		//add missing pages
-		for index in firstNeededPageIndex...lastNeededPageIndex {
-			if !self.isDisplayingPage(forIndex: index) {
-				let page = self.dequeueRecycledPage() ?? NCViewerPhotoImageScrollView()
-				
-				self.configure(page, for: index)
-				self.pagingScrollView.addSubview(page)
-				self.visiblePages.insert(page)
-				
-			}
-		}
-		
-	}
-	
-	func dequeueRecycledPage() -> NCViewerPhotoImageScrollView? {
-		if let page = self.recycledPages.first {
-			self.recycledPages.removeFirst()
-			return page
-		}
-		return nil
-	}
-	
-	
-	func isDisplayingPage(forIndex index: Int) -> Bool {
-		for page in self.visiblePages {
-			if page.index == index {
-				return true
-			}
-		}
-		return false
-	}
-	
-	
-	func configure(_ page: NCViewerPhotoImageScrollView, for index: Int) {
-		self.singleTap.require(toFail: page.zoomingTap)
-		page.backgroundColor = self.view.backgroundColor
-
-		page.index = index
-		page.frame = self.frameForPage(at: index)
-		self.displayImage(at: index, in: page)
-	}
-	
-	func displayImage(at index: Int, in page: NCViewerPhotoImageScrollView) {
-		let imageFileName = self.imageName(at: index)
-		self.currentImageName = imageFileName
-		let imageURL = Bundle.main.url(forResource: imageFileName, withExtension: "jpg")!
-		
-		let tileManager = NCViewerPhotoTileManager()
-
-		if !tileManager.needsTilingImage(in: imageURL) {
-			let image: UIImage! = UIImage(contentsOfFile: imageURL.path)
-			page.display(image)
-			return
-		}
-
-		if tileManager.tilesMadeForImage(named: imageFileName) {
-			let size = tileManager.sizeOfTiledImage(named: imageFileName)!
-			let url = tileManager.urlOfTiledImage(named: imageFileName)
-			page.displayTiledImage(in: url, size: size)
-		}
-		else {
-			
-			if self.inTilingProcess.contains(imageFileName) {
-				if let placeholderURL = tileManager.urlOfPlaceholderOfImage(named: imageFileName) {
-					let image: UIImage! = UIImage(contentsOfFile: placeholderURL.path)
-					page.display(image)
-				}
-				return
-			}
-			else {
-				self.inTilingProcess.insert(imageFileName)
-			}
-
-			tileManager.makeTiledImage(for: imageURL, placeholderCompletion: { (url, error) in
-				if error == nil {
-					let image: UIImage! = UIImage(contentsOfFile: url!.path)
-					page.display(image)
-				}
-				
-			}, tilingCompletion: { (imageName, imageSize, error) in
-				if error == nil, imageName == self.currentImageName {
-					let url = tileManager.urlOfTiledImage(named: imageName!)
-					page.displayTiledImage(in: url, size: imageSize!)
-					
-					if self.inTilingProcess.contains(imageName!) {
-						self.inTilingProcess.remove(imageName!)
-					}
-					
-				}
-				else {
-					if error != nil {
-						print(error!)
-					}
-				}
-			})
-			
-		}
-		
-	}
-	
-	//MARK: - ScrollView delegate methods
-	
-	func scrollViewDidScroll(_ scrollView: UIScrollView) {
-		self.tilePages()
-	}
-	
-	//MARK: - Frame calculations
-	
-	func frameForPagingScrollView(in size: CGSize? = nil) -> CGRect {
-		var frame = UIScreen.main.bounds
-		
-		if size != nil {
-			frame.size = size!
-		}
-		
-		frame.origin.x -= pagePadding
-		frame.size.width += 2*pagePadding
-		return frame
-	}
-	
-	func contentSizeForPagingScrollView() -> CGSize {
-		let bounds = self.pagingScrollView.bounds
-		return CGSize(width: bounds.size.width*CGFloat(self.imageCount), height: bounds.size.height)
-	}
-	
-	func frameForPage(at index: Int) -> CGRect {
-		
-		let bounds = self.pagingScrollView.bounds
-		var pageFrame = bounds
-		pageFrame.size.width -= 2*pagePadding
-		pageFrame.origin.x = (bounds.size.width*CGFloat(index)) + pagePadding
-		
-		return pageFrame
-	}
-	
-	//MARK: - Rotation Configuration
-	
-	override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
-		self.saveCurrentStatesForRotation()
-	}
-	
-	override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
-		self.restoreStatesForRotation(in: size)
-	}
-	
-	/**
-	Save current page and zooming states for device rotation.
-	*/
-	func saveCurrentStatesForRotation() {
-		let visibleBounds = pagingScrollView.bounds
-		firstVisiblePageIndexBeforeRotation = Int(floor(visibleBounds.minX/visibleBounds.width))
-	}
-	
-	/**
-	Apply tracked informations for device rotation.
-	*/
-	func restoreStatesForRotation(in size: CGSize) {
-		// recalculate contentSize based on current orientation
-		let pagingScrollViewFrame = self.frameForPagingScrollView(in: size)
-		pagingScrollView?.frame = pagingScrollViewFrame
-		pagingScrollView?.contentSize = self.contentSizeForPagingScrollView()
-		
-		// adjust frames and configuration of each visible page
-		for page in visiblePages {
-			let restorePoint = page.pointToCenterAfterRotation()
-			let restoreScale = page.scaleToRestoreAfterRotation()
-			page.frame = self.frameForPage(at: page.index)
-			page.setMaxMinZoomScaleForCurrentBounds()
-			page.restoreCenterPoint(to: restorePoint, oldScale: restoreScale)
-		}
-		
-		// adjust contentOffset to preserve page location based on values collected prior to location
-		var contentOffset = CGPoint.zero
-		
-		let pageWidth = pagingScrollView?.bounds.size.width ?? 1
-		contentOffset.x = (CGFloat(firstVisiblePageIndexBeforeRotation) * pageWidth)
-		
-		pagingScrollView?.contentOffset = contentOffset
-		
-	}
-	
-	//MARK: - Handle Tap
-	
-	/// Single tap action which hides navigationBar by default implementation
-	@objc func handleSingleTap() {
-        /*
-        let duration: TimeInterval = 0.2
-         
-		if self.navigationController != nil {
-			
-			if !self.navigationBarIsHidden {
-				
-				self.navigationBarIsHidden = true
-				UIView.animate(withDuration: duration, animations: {
-					self.navigationController!.navigationBar.alpha = 0
-					self.updateBackgroundColor()
-
-				}, completion: { (finished) in
-					self.navigationController!.navigationBar.isHidden = true
-				})
-				
-			}
-			else {
-				self.navigationBarIsHidden = false
-				UIView.animate(withDuration: duration) {
-					self.navigationController!.navigationBar.alpha = 1
-					self.navigationController!.navigationBar.isHidden = false
-					self.updateBackgroundColor()
-				}
-			}
-		}
-        */
-	}
-    
-	/// Update background color. Default is white / black.
-    /*
-	func updateBackgroundColor() {
-		if  !self.navigationBarIsHidden {
-			self.updateBackground(to: .white)
-		}
-		else {
-			self.updateBackground(to: .black)
-		}
-	}
-	*/
-    
-    /*
-	func updateBackground(to color: UIColor) {
-		self.view.backgroundColor = color
-		pagingScrollView?.backgroundColor = color
-		
-		for page in visiblePages {
-			page.backgroundColor = color
-		}
-	}
-	*/
-    
-	func layoutPagingScrollView() {
-		self.pagingScrollView.translatesAutoresizingMaskIntoConstraints = false
-		
-		let top = NSLayoutConstraint(item: self.pagingScrollView!, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1.0, constant: 0.0)
-		let left = NSLayoutConstraint(item: self.pagingScrollView!, attribute: .left, relatedBy: .equal, toItem: self.view, attribute: .left, multiplier: 1.0, constant: -10.0)
-		
-		let bottom = NSLayoutConstraint(item: self.pagingScrollView!, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1.0, constant: 0.0)
-		let right = NSLayoutConstraint(item: self.pagingScrollView!, attribute: .right, relatedBy: .equal, toItem: self.view, attribute: .right, multiplier: 1.0, constant: 10.0)
-		
-		self.view.addConstraints([top, left, bottom, right])
-	}
-	
-	//MARK: - Image Fetching tools
-	
-	lazy var imageData: [Any]? = {
-		var data: [Any]? = nil
-		
-		DispatchQueue.global().sync {
-			let path = Bundle.main.url(forResource: "ImageData", withExtension: "plist")
-			do {
-				let plistData = try Data(contentsOf: path!)
-				data = try PropertyListSerialization.propertyList(from: plistData, options: PropertyListSerialization.ReadOptions.mutableContainers, format: nil) as? [Any]
-				// return data
-			}
-			catch {
-				print("Unable to read image data: ", error)
-			}
-			
-		}
-		return data
-	}()
-	
-	lazy var imageCount: Int = {
-		return self.imageData?.count ?? 0
-	}()
-	
-	func imageName(at index: Int) -> String {
-		if let info = imageData?[index] as? [String: Any] {
-			return info["name"] as? String ?? ""
-		}
-		return ""
-	}
-	
-	// we use "imageWithContentsOfFile:" instead of "imageNamed:" here to avoid caching
-	func image(at index: Int) -> UIImage {
-		let name = imageName(at: index)
-		if let path = Bundle.main.path(forResource: name, ofType: "jpg") {
-			return UIImage(contentsOfFile: path)!
-		}
-		return UIImage()
-	}
-	
-	func imageSizeAt(index: Int) -> CGSize {
-		if let info = imageData?[index] as? [String: Any] {
-			return CGSize(width: info["width"] as? CGFloat ?? 0, height: info["height"] as? CGFloat ?? 0)
-		}
-		return CGSize.zero
-	}
-	
-}