diff --git a/ownCloud.xcodeproj/project.pbxproj b/ownCloud.xcodeproj/project.pbxproj index 3c82e3f6b..2708fed60 100644 --- a/ownCloud.xcodeproj/project.pbxproj +++ b/ownCloud.xcodeproj/project.pbxproj @@ -86,6 +86,7 @@ 6E586CFC2199A72600F680C4 /* OpenInAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E586CFB2199A72600F680C4 /* OpenInAction.swift */; }; 6E586CFE2199A75900F680C4 /* MoveAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E586CFD2199A75900F680C4 /* MoveAction.swift */; }; 6E586D002199A78E00F680C4 /* DeleteAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E586CFF2199A78E00F680C4 /* DeleteAction.swift */; }; + 6E5FC172221590B000F60846 /* GalleryHostViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E5FC171221590B000F60846 /* GalleryHostViewController.swift */; }; 6E83C77E20A32C1B0066EC23 /* SettingsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E83C77D20A32C1B0066EC23 /* SettingsSection.swift */; }; 6E83C78420A33C180066EC23 /* LAContext+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E83C78320A33C180066EC23 /* LAContext+Extension.swift */; }; 6E91F37E21ECA6FD009436D2 /* CopyAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E91F37D21ECA6FD009436D2 /* CopyAction.swift */; }; @@ -546,6 +547,7 @@ 6E586CFB2199A72600F680C4 /* OpenInAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInAction.swift; sourceTree = ""; }; 6E586CFD2199A75900F680C4 /* MoveAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveAction.swift; sourceTree = ""; }; 6E586CFF2199A78E00F680C4 /* DeleteAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteAction.swift; sourceTree = ""; }; + 6E5FC171221590B000F60846 /* GalleryHostViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryHostViewController.swift; sourceTree = ""; }; 6E83C77D20A32C1B0066EC23 /* SettingsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSection.swift; sourceTree = ""; }; 6E83C78320A33C180066EC23 /* LAContext+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LAContext+Extension.swift"; sourceTree = ""; }; 6E91F37D21ECA6FD009436D2 /* CopyAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyAction.swift; sourceTree = ""; }; @@ -771,6 +773,7 @@ 233BDE9E204FEFE500C06732 /* ownCloud */ = { isa = PBXGroup; children = ( + 6E8E3CAB2213287500BF8BDB /* Gallery */, 23EC774D2137F3CD0032D4E6 /* Viewer */, 233BDE9F204FEFE500C06732 /* AppDelegate.swift */, DCF4F1612051925A00189B9A /* Bookmarks */, @@ -970,6 +973,14 @@ path = "Actions+Extensions"; sourceTree = ""; }; + 6E8E3CAB2213287500BF8BDB /* Gallery */ = { + isa = PBXGroup; + children = ( + 6E5FC171221590B000F60846 /* GalleryHostViewController.swift */, + ); + path = Gallery; + sourceTree = ""; + }; DC1B26FD209CF0D2004715E1 /* Issues Animators */ = { isa = PBXGroup; children = ( @@ -1855,6 +1866,7 @@ 232B01F62126B10900366FA0 /* MoreStaticTableViewController.swift in Sources */, 6E91F37E21ECA6FD009436D2 /* CopyAction.swift in Sources */, 593BAB97209F8A0500023634 /* AppLockManager.swift in Sources */, + 6E5FC172221590B000F60846 /* GalleryHostViewController.swift in Sources */, DC85493421831B0B00782BA8 /* Tools.swift in Sources */, DCFED972208095E200A2D984 /* ClientItemCell.swift in Sources */, 23E22BB720C6A5C40024D11E /* UIDevice+UIUserInterfaceIdiom.swift in Sources */, diff --git a/ownCloud/Client/ClientQueryViewController.swift b/ownCloud/Client/ClientQueryViewController.swift index 6ae42818a..2ba76fdba 100644 --- a/ownCloud/Client/ClientQueryViewController.swift +++ b/ownCloud/Client/ClientQueryViewController.swift @@ -303,16 +303,21 @@ class ClientQueryViewController: UITableViewController, Themeable { lastTappedItemLocalID = rowItem.localID core.downloadItem(rowItem, options: [ .returnImmediatelyIfOfflineOrUnavailable : true ]) { [weak self, query] (error, core, item, _) in + + guard let self = self else { return } OnMainThread { if (error == nil) || (error as NSError?)?.isOCError(withCode: .itemNotAvailableOffline) == true { - if let item = item, item.localID == self?.lastTappedItemLocalID, let core = core { - let itemViewController = DisplayHostViewController(for: item, with: core, root: query.rootItem!) - self?.navigationController?.pushViewController(itemViewController, animated: true) + if let item = item, let core = core { + if item.localID == self.lastTappedItemLocalID { + let itemViewController = GalleryHostViewController(core: core, selectedItem: item, query: query) + itemViewController.hidesBottomBarWhenPushed = true + self.navigationController?.pushViewController(itemViewController, animated: true) + } } } - if self?.lastTappedItemLocalID == item?.localID { - self?.lastTappedItemLocalID = nil + if self.lastTappedItemLocalID == item?.localID { + self.lastTappedItemLocalID = nil } } } diff --git a/ownCloud/Client/ClientRootViewController.swift b/ownCloud/Client/ClientRootViewController.swift index 053a3cb14..ee8ceeced 100644 --- a/ownCloud/Client/ClientRootViewController.swift +++ b/ownCloud/Client/ClientRootViewController.swift @@ -32,6 +32,7 @@ class ClientRootViewController: UITabBarController, UINavigationControllerDelega var activityNavigationController : ThemeNavigationController? var activityViewController : ClientActivityViewController? var progressBar : CollapsibleProgressBar? + var progressBarHeightConstraint: NSLayoutConstraint? var progressSummarizer : ProgressSummarizer? var toolbar : UIToolbar? @@ -177,7 +178,8 @@ class ClientRootViewController: UITabBarController, UINavigationControllerDelega progressBar?.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true progressBar?.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true - progressBar?.bottomAnchor.constraint(equalTo: self.tabBar.topAnchor).isActive = true + progressBarHeightConstraint = NSLayoutConstraint(item: progressBar!, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1.0, constant: -1 * (self.tabBar.bounds.height)) + progressBarHeightConstraint?.isActive = true toolbar = UIToolbar(frame: .zero) toolbar?.translatesAutoresizingMaskIntoConstraints = false @@ -185,8 +187,8 @@ class ClientRootViewController: UITabBarController, UINavigationControllerDelega self.view.addSubview(toolbar!) - toolbar?.leftAnchor.constraint(equalTo: self.tabBar.leftAnchor).isActive = true - toolbar?.rightAnchor.constraint(equalTo: self.tabBar.rightAnchor).isActive = true + toolbar?.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true + toolbar?.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true toolbar?.topAnchor.constraint(equalTo: self.tabBar.topAnchor).isActive = true toolbar?.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor).isActive = true @@ -220,6 +222,13 @@ class ClientRootViewController: UITabBarController, UINavigationControllerDelega self.closeClient() } } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + progressBarHeightConstraint?.constant = -1 * (self.tabBar.bounds.height) + self.progressBar?.setNeedsLayout() +// self.view.setNeedsLayout() + } } extension ClientRootViewController : Themeable { diff --git a/ownCloud/Gallery/GalleryHostViewController.swift b/ownCloud/Gallery/GalleryHostViewController.swift new file mode 100644 index 000000000..db9d5d9b8 --- /dev/null +++ b/ownCloud/Gallery/GalleryHostViewController.swift @@ -0,0 +1,231 @@ +// +// AlternativePageViewController.swift +// ownCloud +// +// Created by Pablo Carrascal on 14/02/2019. +// Copyright © 2019 ownCloud GmbH. All rights reserved. +// + +import UIKit +import ownCloudSDK + +class GalleryHostViewController: UIPageViewController { + + typealias Filter = ([OCItem]) -> [OCItem] + + // MARK: - Constants + let hasChangesAvailableKeyPath: String = "hasChangesAvailable" + let imageFilterRegexp: String = "\\A((image/*))" // Filters all the mime types that are images (incluiding gif and svg) + + // MARK: - Instance Variables + weak private var core: OCCore? + private var selectedItem: OCItem + private var items: [OCItem]? + private var query: OCQuery + private weak var viewControllerToTansition: DisplayViewController? + private var selectedFilter: Filter? + + // MARK: - Filters + lazy var filterImageFiles: Filter = { items in + let filteredItems = items.filter({$0.type != .collection && $0.mimeType?.matches(regExp: self.imageFilterRegexp) ?? false}) + return filteredItems + } + + lazy var filterOneItem: Filter = { items in + let filteredItems = items.filter({$0.type != .collection && $0.fileID == self.selectedItem.fileID}) + return filteredItems + } + + // MARK: - Init & deinit + init(core: OCCore, selectedItem: OCItem, query: OCQuery) { + self.core = core + self.selectedItem = selectedItem + self.query = query + + super.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil) + query.addObserver(self, forKeyPath: hasChangesAvailableKeyPath, options: [.initial, .new], context: nil) + Theme.shared.register(client: self) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + query.removeObserver(self, forKeyPath: hasChangesAvailableKeyPath) + Theme.shared.unregister(client: self) + } + + // MARK: - ViewController lifecycle + override func viewDidLoad() { + super.viewDidLoad() + dataSource = self + delegate = self + + if selectedItem.mimeType?.matches(regExp: imageFilterRegexp) ?? false { + selectedFilter = filterImageFiles + } else { + selectedFilter = filterOneItem + } + + query.requestChangeSet(withFlags: .onlyResults) { [weak self] ( _, changeSet) in + guard let self = self else { return} + guard let queryResult = changeSet?.queryResult else { return } + + self.items = self.selectedFilter?(queryResult) + + if let items = self.items, let index = items.firstIndex(where: {$0.fileID == self.selectedItem.fileID}) { + let itemToDisplay = items[index] + + guard let mimeType = itemToDisplay.mimeType else { return } + OnMainThread { + let viewController = self.selectDisplayViewControllerBasedOn(mimeType: mimeType) + let configuration = self.configurationFor(itemToDisplay, viewController: viewController) + + viewController.configure(configuration) + self.addChild(viewController) + viewController.didMove(toParent: self) + + self.setViewControllers([viewController], direction: .forward, animated: false, completion: nil) + + viewController.present(item: itemToDisplay) + viewController.updateNavigationBarItems() + } + } + } + } + + override var childForHomeIndicatorAutoHidden : UIViewController? { + if let childViewController = self.children.first { + return childViewController + } + return nil + } + + // swiftlint:disable block_based_kvo + // Would love to use the block-based KVO, but it doesn't seem to work when used on the .state property of the query :-( + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + if (object as? OCQuery) === query { + query.requestChangeSet(withFlags: .onlyResults) { ( _, changeSet) in + guard changeSet != nil else { return } + self.items = self.selectedFilter?(changeSet!.queryResult) + } + } + } + // swiftlint:enable block_based_kvo + + // MARK: - Extension selection + private func selectDisplayViewControllerBasedOn(mimeType: String) -> (DisplayViewController) { + + let locationIdentifier = OCExtensionLocationIdentifier(rawValue: mimeType) + let location: OCExtensionLocation = OCExtensionLocation(ofType: .viewer, identifier: locationIdentifier) + let context = OCExtensionContext(location: location, requirements: nil, preferences: nil) + + var extensions: [OCExtensionMatch]? + + do { + try extensions = OCExtensionManager.shared.provideExtensions(for: context) + } catch { + return DisplayViewController() + } + + guard let matchedExtensions = extensions else { + return DisplayViewController() + } + + guard matchedExtensions.count > 0 else { + return DisplayViewController() + } + + let preferedExtension: OCExtension = matchedExtensions[0].extension + + let extensionObject = preferedExtension.provideObject(for: context) + + guard let controllerType = extensionObject as? (DisplayViewController & DisplayExtension) else { + return DisplayViewController() + } + + return controllerType + } + + // MARK: - Helper methods + private func viewControllerAtIndex(index: Int) -> UIViewController? { + guard let items = items else { return nil } + + guard index >= 0, index < items.count else { return nil } + + let item = items[index] + + let newViewController = selectDisplayViewControllerBasedOn(mimeType: item.mimeType!) + let configuration = configurationFor(item, viewController: newViewController) + + newViewController.configure(configuration) + newViewController.present(item: item) + return newViewController + } + + private func configurationFor(_ item: OCItem, viewController: UIViewController) -> DisplayViewConfiguration { + let shouldDownload = viewController is (DisplayViewController & DisplayExtension) ? true : false + var configuration: DisplayViewConfiguration + if !shouldDownload { + configuration = DisplayViewConfiguration(item: item, core: core, state: .notSupportedMimeType) + } else { + configuration = DisplayViewConfiguration(item: item, core: core, state: .hasNetworkConnection) + } + return configuration + } +} + +extension GalleryHostViewController: UIPageViewControllerDataSource { + func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { + if let displayViewController = viewControllers?.first as? DisplayViewController, + let item = displayViewController.item, + let index = items?.firstIndex(where: {$0.fileID == item.fileID}) { + return viewControllerAtIndex(index: index + 1) + } + + return nil + + } + + func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { + + if let displayViewController = viewControllers?.first as? DisplayViewController, + let item = displayViewController.item, + let index = items?.firstIndex(where: {$0.fileID == item.fileID}) { + return viewControllerAtIndex(index: index - 1) + } + + return nil + } +} + +extension GalleryHostViewController: UIPageViewControllerDelegate { + func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { + + let previousViewController = previousViewControllers[0] + previousViewController.didMove(toParent: nil) + + if completed, let viewControllerToTransition = self.viewControllerToTansition { + if self.children.contains(viewControllerToTransition) == false { + self.addChild(viewControllerToTransition) + } + viewControllerToTransition.didMove(toParent: self) + viewControllerToTransition.updateNavigationBarItems() + } + } + + func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) { + guard pendingViewControllers.isEmpty == false else { return } + + if let viewControllerToTransition = pendingViewControllers[0] as? DisplayViewController { + self.viewControllerToTansition = viewControllerToTransition + } + } +} + +extension GalleryHostViewController: Themeable { + func applyThemeCollection(theme: Theme, collection: ThemeCollection, event: ThemeEvent) { + self.view.backgroundColor = .black + } +} diff --git a/ownCloud/UI Elements/ImageScrollView.swift b/ownCloud/UI Elements/ImageScrollView.swift index 599dc2284..53535b170 100644 --- a/ownCloud/UI Elements/ImageScrollView.swift +++ b/ownCloud/UI Elements/ImageScrollView.swift @@ -102,11 +102,11 @@ extension ImageScrollView { setNeedsLayout() } - func display(image: UIImage) { + func display(image: UIImage, inSize: CGSize) { imageView?.removeFromSuperview() imageView = UIImageView(image: image) addSubview(imageView) - updateScaleForRotation(size: self.bounds.size) + updateScaleForRotation(size: inSize) } } diff --git a/ownCloud/UIKit Extensions/String+Extension.swift b/ownCloud/UIKit Extensions/String+Extension.swift index 1857a82a8..11b741bd3 100644 --- a/ownCloud/UIKit Extensions/String+Extension.swift +++ b/ownCloud/UIKit Extensions/String+Extension.swift @@ -28,4 +28,10 @@ extension String { let nonDigitsCharacterSet = CharacterSet.decimalDigits.inverted return !self.isEmpty && rangeOfCharacter(from: nonDigitsCharacterSet) == nil } + + func matches (regExp: String) -> Bool { + guard let regex = try? NSRegularExpression(pattern: regExp) else { return false } + let range = NSRange(location: 0, length: self.count) + return regex.firstMatch(in: self, options: [], range: range) != nil + } } diff --git a/ownCloud/Viewer/DisplayViewController.swift b/ownCloud/Viewer/DisplayViewController.swift index 94bbf3926..eb52bc0b6 100644 --- a/ownCloud/Viewer/DisplayViewController.swift +++ b/ownCloud/Viewer/DisplayViewController.swift @@ -20,7 +20,7 @@ import UIKit import ownCloudSDK struct DisplayViewConfiguration { - weak var item: OCItem! + var item: OCItem! weak var core: OCCore! let state: DisplayViewState } @@ -46,7 +46,7 @@ class DisplayViewController: UIViewController, OCQueryDelegate { private let progressViewVerticalSpacing: CGFloat = 20.0 // MARK: - Configuration - weak var item: OCItem? + var item: OCItem? weak var core: OCCore? { willSet { if let core = core { @@ -64,9 +64,9 @@ class DisplayViewController: UIViewController, OCQueryDelegate { didSet { OnMainThread { self.iconImageView.isHidden = true + self.hideItemMetadataUIElements() + self.renderSpecificView() } - hideItemMetadataUIElements() - renderSpecificView() } } @@ -104,6 +104,12 @@ class DisplayViewController: UIViewController, OCQueryDelegate { // MARK: - Init & Deinit required init() { + iconImageView = UIImageView() + metadataInfoLabel = UILabel() + cancelButton = ThemeButton(type: .custom) + showPreviewButton = ThemeButton(type: .custom) + noNetworkLabel = UILabel() + super.init(nibName: nil, bundle: nil) } @@ -124,13 +130,11 @@ class DisplayViewController: UIViewController, OCQueryDelegate { override func loadView() { super.loadView() - iconImageView = UIImageView() iconImageView.translatesAutoresizingMaskIntoConstraints = false iconImageView.contentMode = .scaleAspectFit view.addSubview(iconImageView) - metadataInfoLabel = UILabel() metadataInfoLabel?.translatesAutoresizingMaskIntoConstraints = false metadataInfoLabel?.isHidden = false metadataInfoLabel?.textAlignment = .center @@ -147,7 +151,6 @@ class DisplayViewController: UIViewController, OCQueryDelegate { view.addSubview(progressView!) - cancelButton = ThemeButton(type: .custom) cancelButton?.translatesAutoresizingMaskIntoConstraints = false cancelButton?.setTitle("Cancel".localized, for: .normal) cancelButton?.isHidden = (downloadProgress != nil) @@ -155,14 +158,12 @@ class DisplayViewController: UIViewController, OCQueryDelegate { view.addSubview(cancelButton!) - showPreviewButton = ThemeButton(type: .custom) showPreviewButton?.translatesAutoresizingMaskIntoConstraints = false showPreviewButton?.setTitle("Open file".localized, for: .normal) showPreviewButton?.isHidden = true showPreviewButton?.addTarget(self, action: #selector(downloadItem), for: UIControl.Event.touchUpInside) view.addSubview(showPreviewButton!) - noNetworkLabel = UILabel() noNetworkLabel?.translatesAutoresizingMaskIntoConstraints = false noNetworkLabel?.isHidden = true noNetworkLabel?.adjustsFontForContentSizeCategory = true @@ -235,14 +236,16 @@ class DisplayViewController: UIViewController, OCQueryDelegate { }) } } + } + } - if let parent = parent, let itemName = item.name { - parent.navigationItem.title = itemName + func updateNavigationBarItems() { + if let parent = parent, let itemName = item?.name { + parent.navigationItem.title = itemName - let actionsBarButtonItem = UIBarButtonItem(title: "•••", style: .plain, target: self, action: #selector(optionsBarButtonPressed)) - actionsBarButtonItem.accessibilityLabel = itemName + " " + "Actions".localized - parent.navigationItem.rightBarButtonItem = actionsBarButtonItem - } + let actionsBarButtonItem = UIBarButtonItem(title: "•••", style: .plain, target: self, action: #selector(optionsBarButtonPressed)) + actionsBarButtonItem.accessibilityLabel = itemName + " " + "Actions".localized + parent.navigationItem.rightBarButtonItem = actionsBarButtonItem } } @@ -270,10 +273,8 @@ class DisplayViewController: UIViewController, OCQueryDelegate { } return } - OnMainThread { self?.item = latestItem self?.source = file!.url - } }) { self.state = .downloading(progress: downloadProgress) } @@ -437,8 +438,8 @@ class DisplayViewController: UIViewController, OCQueryDelegate { // MARK: - Public API extension DisplayViewController { func configure(_ configuration: DisplayViewConfiguration) { - self.core = configuration.core self.item = configuration.item + self.core = configuration.core self.state = configuration.state } } @@ -451,5 +452,6 @@ extension DisplayViewController : Themeable { metadataInfoLabel?.applyThemeCollection(collection) showPreviewButton?.applyThemeCollection(collection) noNetworkLabel?.applyThemeCollection(collection) + self.view.backgroundColor = collection.tableBackgroundColor } } diff --git a/ownCloud/Viewer/ImageDisplayViewController.swift b/ownCloud/Viewer/ImageDisplayViewController.swift index 47c1a93e0..36ef430cc 100644 --- a/ownCloud/Viewer/ImageDisplayViewController.swift +++ b/ownCloud/Viewer/ImageDisplayViewController.swift @@ -21,33 +21,90 @@ import ownCloudSDK class ImageDisplayViewController : DisplayViewController { - private let MAX_ZOOM_DIVIDER: CGFloat = 3.0 + private let max_zoom_divider: CGFloat = 3.0 + private let activityIndicatorHeight: CGFloat = 50.0 + + private let serialQueue: DispatchQueue = DispatchQueue(label: "decode queue") // MARK: - Instance variables var scrollView: ImageScrollView? var imageView: UIImageView? + var activityIndicatorView: UIActivityIndicatorView = { + let activityIndicator = UIActivityIndicatorView(style: UIActivityIndicatorView.Style.white) + activityIndicator.translatesAutoresizingMaskIntoConstraints = false + return activityIndicator + }() // MARK: - Gesture recognizers var tapToZoomGestureRecognizer : UITapGestureRecognizer! var tapToHideBarsGestureRecognizer: UITapGestureRecognizer! + // MARK: - View controller lifecycle + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + + guard let navigationController = navigationController else { + return + } + + navigationController.setNavigationBarHidden(false, animated: true) + + setNeedsUpdateOfHomeIndicatorAutoHidden() + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidAppear(animated) + + scrollView?.setZoomScale(scrollView!.minimumZoomScale, animated: true) + } + + func downSampleImage() { + if let source = source { + activityIndicatorView.startAnimating() + let size: CGSize = self.view.bounds.size + let scale: CGFloat = UIScreen.main.scale + let imageSourceOptions = [kCGImageSourceShouldCache: true] as CFDictionary + let imageSource = CGImageSourceCreateWithURL(source as CFURL, imageSourceOptions)! + let maxDimensionInPixels = max(size.width, size.height) * scale + let downsampleOptions = [kCGImageSourceCreateThumbnailFromImageAlways: true, + kCGImageSourceShouldCacheImmediately: true, + kCGImageSourceCreateThumbnailWithTransform: true, + kCGImageSourceThumbnailMaxPixelSize: maxDimensionInPixels] as CFDictionary + serialQueue.async { + let downsampledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, downsampleOptions)! + let image = UIImage(cgImage: downsampledImage) + OnMainThread { + self.activityIndicatorView.stopAnimating() + self.scrollView?.display(image: image, inSize: self.view.bounds.size) + } + } + } + + } + // MARK: - Specific view override func renderSpecificView() { - scrollView = ImageScrollView(frame: self.view.bounds) + scrollView = ImageScrollView(frame: .zero) scrollView?.translatesAutoresizingMaskIntoConstraints = false + self.view.addSubview(scrollView!) NSLayoutConstraint.activate([ scrollView!.leftAnchor.constraint(equalTo: view.leftAnchor), scrollView!.rightAnchor.constraint(equalTo: view.rightAnchor), scrollView!.bottomAnchor.constraint(equalTo: view.bottomAnchor), scrollView!.topAnchor.constraint(equalTo: view.topAnchor) - ]) - - if let source = source, - let data = try? Data(contentsOf: source), - let image = UIImage(data: data) { - scrollView?.display(image: image) + ]) + self.scrollView?.addSubview(activityIndicatorView) + NSLayoutConstraint.activate([ + activityIndicatorView.centerYAnchor.constraint(equalTo: scrollView!.centerYAnchor), + activityIndicatorView.centerXAnchor.constraint(equalTo: scrollView!.centerXAnchor), + activityIndicatorView.heightAnchor.constraint(equalToConstant: activityIndicatorHeight), + activityIndicatorView.widthAnchor.constraint(equalTo: activityIndicatorView.heightAnchor) + ]) + + if source != nil { + downSampleImage() tapToZoomGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapToZoom)) tapToZoomGestureRecognizer.numberOfTapsRequired = 2 scrollView?.addGestureRecognizer(tapToZoomGestureRecognizer) @@ -76,7 +133,7 @@ class ImageDisplayViewController : DisplayViewController { if scrollView!.zoomScale != scrollView!.minimumZoomScale { scrollView!.setZoomScale(scrollView!.minimumZoomScale, animated: true) } else { - scrollView!.setZoomScale(scrollView!.maximumZoomScale / MAX_ZOOM_DIVIDER, animated: true) + scrollView!.setZoomScale(scrollView!.maximumZoomScale / max_zoom_divider, animated: true) } setNeedsUpdateOfHomeIndicatorAutoHidden() diff --git a/ownCloud/Viewer/WebViewDisplayViewController.swift b/ownCloud/Viewer/WebViewDisplayViewController.swift index 7dd5c7a3d..02c86c92e 100644 --- a/ownCloud/Viewer/WebViewDisplayViewController.swift +++ b/ownCloud/Viewer/WebViewDisplayViewController.swift @@ -7,14 +7,14 @@ // /* - * Copyright (C) 2018, ownCloud GmbH. - * - * This code is covered by the GNU Public License Version 3. - * - * For distribution utilizing Apple mechanisms please see https://owncloud.org/contribute/iOS-license-exception/ - * You should have received a copy of this license along with this program. If not, see . - * - */ +* Copyright (C) 2018, ownCloud GmbH. +* +* This code is covered by the GNU Public License Version 3. +* +* For distribution utilizing Apple mechanisms please see https://owncloud.org/contribute/iOS-license-exception/ +* You should have received a copy of this license along with this program. If not, see . +* +*/ import UIKit import ownCloudSDK @@ -23,6 +23,9 @@ import WebKit class WebViewDisplayViewController: DisplayViewController { var webView: WKWebView? + lazy var fullScreenGesture: UITapGestureRecognizer = { + return UITapGestureRecognizer(target: self, action: #selector(self.tapToFullScreen)) + }() override func renderSpecificView() { WebViewDisplayViewController.externalContentBlockingRuleList { (blockList, error) in @@ -53,9 +56,8 @@ class WebViewDisplayViewController: DisplayViewController { self.webView?.loadFileURL(source, allowingReadAccessTo: source) - let fullScreenGesture = UITapGestureRecognizer(target: self, action: #selector(self.tapToFullScreen)) - fullScreenGesture.delegate = self - self.webView?.addGestureRecognizer(fullScreenGesture) + self.fullScreenGesture.delegate = self + self.webView?.addGestureRecognizer(self.fullScreenGesture) } } } @@ -130,6 +132,10 @@ extension WebViewDisplayViewController: DisplayExtension { extension WebViewDisplayViewController: UIGestureRecognizerDelegate { func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { + if otherGestureRecognizer == fullScreenGesture { + return false + } + return true } }