// LoadingLargeImageViewController.swift // Tusker // // Created by Shadowfacts on 6/14/19. // Copyright © 2019 Shadowfacts. All rights reserved. // import UIKit import Pachyderm import TuskerComponents class LoadingLargeImageViewController: UIViewController, LargeImageAnimatableViewController { private var attachment: Attachment? let url: URL let cache: ImageCache let imageDescription: String? private(set) var loaded = false private(set) var largeImageVC: LargeImageViewController? private var loadingVC: LoadingViewController? private var imageRequest: ImageCache.Request? private var initialControlsVisible: Bool = true var controlsVisible: Bool { get { return largeImageVC?.controlsVisible ?? initialControlsVisible } set { if let largeImageVC = largeImageVC { largeImageVC.setControlsVisible(newValue, animated: false) } else { initialControlsVisible = newValue } } } var shrinkGestureEnabled = true weak var animationSourceView: UIImageView? var largeImageController: LargeImageViewController? { largeImageVC } var animationImage: UIImage? { largeImageVC?.animationImage ?? animationSourceView?.image } var dismissInteractionController: LargeImageInteractionController? var isInteractivelyAnimatingDismissal: Bool = false { didSet { setNeedsStatusBarAppearanceUpdate() } } override var prefersStatusBarHidden: Bool { return !isInteractivelyAnimatingDismissal } override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation { return .none } override var childForHomeIndicatorAutoHidden: UIViewController? { return largeImageVC } override var supportedInterfaceOrientations: UIInterfaceOrientationMask { if UIDevice.current.userInterfaceIdiom == .phone { return .allButUpsideDown } else { return .all } } init(url: URL, cache: ImageCache, imageDescription: String?) { self.url = url self.cache = cache self.imageDescription = imageDescription super.init(nibName: nil, bundle: nil) modalPresentationStyle = .fullScreen } convenience init(attachment: Attachment) { self.init(url: attachment.url, cache: .attachments, imageDescription: attachment.description) self.attachment = attachment } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() overrideUserInterfaceStyle = .dark view.backgroundColor = .black // always load full resolution from disk for large image, in case the cache is scaled if let entry = cache.get(url, loadOriginal: true) { // todo: if load original is true, is there any way entry.data could be nil? // feels like the data param of createLargeImage shouldn't be optional createLargeImage(data: entry.data, image: entry.image, url: url) } else { createPreview() loadingVC = LoadingViewController() embedChild(loadingVC!) imageRequest = cache.get(url, loadOriginal: true) { [weak self] (data, image) in guard let self = self, let image = image else { return } self.imageRequest = nil DispatchQueue.main.async { self.loadingVC?.removeViewAndController() self.createLargeImage(data: data, image: image, url: self.url) } } } if shrinkGestureEnabled { dismissInteractionController = LargeImageInteractionController(viewController: self) } } override func didMove(toParent parent: UIViewController?) { super.didMove(toParent: parent) if parent == nil { imageRequest?.cancel() } } private func createLargeImage(data: Data?, image: UIImage, url: URL) { guard !loaded else { return } loaded = true let content: LargeImageContentView // todo: p sure grayscaling gifs has never worked if url.pathExtension == "gif", let data = data { // todo: pulling the gif controller out of the source view feels icky // is it possible for the source view's gif controller to have different data than we just got? // should this be a property set by the animation controller instead? let gifController = (animationSourceView as? GIFImageView)?.gifController ?? GIFController(gifData: data) content = LargeImageGifContentView(url: url, gifController: gifController) } else { if let transformedImage = ImageGrayscalifier.convertIfNecessary(url: url, image: image) { content = LargeImageImageContentView(url: url, data: data, image: transformedImage) } else { content = LargeImageImageContentView(url: url, data: data, image: image) } } setContent(content) } private func setContent(_ content: LargeImageContentView) { if let existing = largeImageVC { existing.contentView = content } else { largeImageVC = LargeImageViewController(contentView: content, description: imageDescription, sourceView: animationSourceView) largeImageVC!.initialControlsVisible = initialControlsVisible largeImageVC!.shrinkGestureEnabled = false embedChild(largeImageVC!) } } private func createPreview() { guard !self.loaded, var image = animationSourceView?.image else { return } if Preferences.shared.grayscaleImages, let source = image.cgImage, let grayscale = ImageGrayscalifier.convert(url: nil, cgImage: source) { image = grayscale } setContent(LargeImageImageContentView(url: url, data: nil, image: image)) } }