You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
233 lines
8.4 KiB
233 lines
8.4 KiB
// |
|
// LargeImageViewController.swift |
|
// Tusker |
|
// |
|
// Created by Shadowfacts on 8/31/18. |
|
// Copyright © 2018 Shadowfacts. All rights reserved. |
|
// |
|
|
|
import UIKit |
|
import Photos |
|
import Gifu |
|
|
|
class LargeImageViewController: UIViewController, UIScrollViewDelegate { |
|
|
|
let router: AppRouter |
|
|
|
var originFrame: CGRect? |
|
var originCornerRadius: CGFloat? |
|
var dismissInteractionController: LargeImageInteractionController? |
|
|
|
@IBOutlet weak var scrollView: UIScrollView! |
|
@IBOutlet weak var imageView: GIFImageView! |
|
@IBOutlet weak var imageViewLeadingConstraint: NSLayoutConstraint! |
|
@IBOutlet weak var imageViewTrailingConstraint: NSLayoutConstraint! |
|
@IBOutlet weak var imageViewTopConstraint: NSLayoutConstraint! |
|
@IBOutlet weak var imageViewBottomConstraint: NSLayoutConstraint! |
|
|
|
@IBOutlet weak var topControlsView: UIView! |
|
@IBOutlet weak var topControlsHeightConstraint: NSLayoutConstraint! |
|
@IBOutlet weak var downloadButton: UIButton! |
|
@IBOutlet weak var downloadButtonTopConstraint: NSLayoutConstraint! |
|
@IBOutlet weak var downloadButtonLeadingConstraint: NSLayoutConstraint! |
|
@IBOutlet weak var closeButton: UIButton! |
|
@IBOutlet weak var closeButtonTopConstraint: NSLayoutConstraint! |
|
@IBOutlet weak var closeButtonTrailingConstraint: NSLayoutConstraint! |
|
|
|
@IBOutlet weak var bottomControlsView: UIView! |
|
@IBOutlet weak var descriptionLabel: UILabel! |
|
|
|
var initializedTopControlsConstrains = false |
|
|
|
var image: UIImage? |
|
var gifData: Data? |
|
var imageDescription: String? |
|
|
|
var controlsVisible = true { |
|
didSet { |
|
UIView.animate(withDuration: 0.2) { |
|
let topOffset = self.controlsVisible ? 0 : -self.topControlsView.bounds.height |
|
self.topControlsView.transform = CGAffineTransform(translationX: 0, y: topOffset) |
|
if self.imageDescription != nil { |
|
let bottomOffset = self.controlsVisible ? 0 : self.bottomControlsView.bounds.height + self.view.safeAreaInsets.bottom |
|
self.bottomControlsView.transform = CGAffineTransform(translationX: 0, y: bottomOffset) |
|
} |
|
} |
|
} |
|
} |
|
|
|
var prevZoomScale: CGFloat? |
|
|
|
override var prefersStatusBarHidden: Bool { |
|
return true |
|
} |
|
|
|
init(image: UIImage, description: String?, sourceFrame: CGRect, sourceCornerRadius: CGFloat, router: AppRouter) { |
|
self.router = router |
|
self.image = image |
|
self.imageDescription = description |
|
self.originFrame = sourceFrame |
|
self.originCornerRadius = sourceCornerRadius |
|
|
|
super.init(nibName: "LargeImageViewController", bundle: nil) |
|
} |
|
|
|
// init(gifData: Data?, description: String?, sourceFrame: CGRect, sourceCornerRadius: CGFloat, router: AppRouter) { |
|
// self.router = router |
|
// self.gifData = gifData |
|
// self.imageDescription = description |
|
// self.originFrame = sourceFrame |
|
// self.originCornerRadius = sourceCornerRadius |
|
// |
|
// super.init(nibName: "LargeImageViewController", bundle: nil) |
|
// } |
|
|
|
required init?(coder aDecoder: NSCoder) { |
|
fatalError("init(coder:) has not been implemented") |
|
} |
|
|
|
override func viewDidLoad() { |
|
super.viewDidLoad() |
|
|
|
imageView.image = image |
|
if let gifData = gifData { |
|
imageView.animate(withGIFData: gifData) |
|
} |
|
|
|
scrollView.delegate = self |
|
imageView.bounds = CGRect(origin: .zero, size: imageView.image!.size) |
|
|
|
if let imageDescription = imageDescription { |
|
descriptionLabel.text = imageDescription |
|
} else { |
|
bottomControlsView.isHidden = true |
|
} |
|
|
|
dismissInteractionController = LargeImageInteractionController(viewController: self) |
|
|
|
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(scrollViewPressed(_:)))) |
|
let doubleTap = UITapGestureRecognizer(target: self, action: #selector(scrollViewDoubleTapped(_:))) |
|
doubleTap.numberOfTapsRequired = 2 |
|
view.addGestureRecognizer(doubleTap) |
|
} |
|
|
|
|
|
override func viewDidLayoutSubviews() { |
|
super.viewDidLayoutSubviews() |
|
|
|
let widthScale = view.bounds.size.width / imageView.bounds.width |
|
let heightScale = view.bounds.size.height / imageView.bounds.height |
|
let minScale = min(widthScale, heightScale) |
|
scrollView.minimumZoomScale = minScale |
|
scrollView.zoomScale = minScale |
|
scrollView.maximumZoomScale = minScale >= 1 ? minScale + 2 : 2 |
|
|
|
centerImage() |
|
|
|
if !initializedTopControlsConstrains { |
|
initializedTopControlsConstrains = true |
|
if view.safeAreaInsets.top == 44 { |
|
// running on iPhone X style notched device |
|
let notchWidth: CGFloat = 209 |
|
let earWidth = (view.bounds.width - notchWidth) / 2 |
|
let offset = (earWidth - downloadButton.bounds.width) / 2 |
|
downloadButtonLeadingConstraint.constant = offset |
|
closeButtonTrailingConstraint.constant = offset |
|
} |
|
} |
|
} |
|
|
|
func viewForZooming(in scrollView: UIScrollView) -> UIView? { |
|
return imageView |
|
} |
|
|
|
func scrollViewDidZoom(_ scrollView: UIScrollView) { |
|
centerImage() |
|
|
|
let prevZoomScale = self.prevZoomScale ?? scrollView.minimumZoomScale |
|
if scrollView.zoomScale <= scrollView.minimumZoomScale { |
|
controlsVisible = true |
|
} else if scrollView.zoomScale > prevZoomScale { |
|
controlsVisible = false |
|
} |
|
self.prevZoomScale = scrollView.zoomScale |
|
} |
|
|
|
func centerImage() { |
|
let yOffset = max(0, (view.bounds.size.height - imageView.frame.height) / 2) |
|
imageViewTopConstraint.constant = yOffset |
|
imageViewBottomConstraint.constant = yOffset |
|
|
|
let xOffset = max(0, (view.bounds.size.width - imageView.frame.width) / 2) |
|
imageViewLeadingConstraint.constant = xOffset |
|
imageViewTrailingConstraint.constant = xOffset |
|
} |
|
|
|
func zoomRectFor(scale: CGFloat, center: CGPoint) -> CGRect { |
|
var zoomRect = CGRect.zero |
|
zoomRect.size.width = imageView.frame.width / scale |
|
zoomRect.size.height = imageView.frame.height / scale |
|
let newCenter = scrollView.convert(center, to: imageView) |
|
zoomRect.origin.x = newCenter.x - (zoomRect.width / 2) |
|
zoomRect.origin.y = newCenter.y - (zoomRect.height / 2) |
|
return zoomRect |
|
} |
|
|
|
func animateZoomOut() { |
|
UIView.animate(withDuration: 0.3, animations: { |
|
self.scrollView.zoomScale = self.scrollView.minimumZoomScale |
|
self.view.layoutIfNeeded() |
|
}) |
|
} |
|
|
|
@objc func scrollViewPressed(_ sender: UITapGestureRecognizer) { |
|
if scrollView.zoomScale > scrollView.minimumZoomScale { |
|
animateZoomOut() |
|
} else { |
|
controlsVisible = !controlsVisible |
|
} |
|
} |
|
|
|
@objc func scrollViewDoubleTapped(_ recognizer: UITapGestureRecognizer) { |
|
if scrollView.zoomScale <= scrollView.minimumZoomScale { |
|
let point = recognizer.location(in: recognizer.view) |
|
let scale: CGFloat |
|
if scrollView.minimumZoomScale < 1 { |
|
if 1 - scrollView.zoomScale <= 0.5 { |
|
scale = scrollView.zoomScale + 1 |
|
} else { |
|
scale = 1 |
|
} |
|
} else { |
|
scale = scrollView.maximumZoomScale |
|
} |
|
let rect = zoomRectFor(scale: scale, center: point) |
|
UIView.animate(withDuration: 0.3) { |
|
self.scrollView.zoom(to: rect, animated: false) |
|
self.view.layoutIfNeeded() |
|
} |
|
} else { |
|
animateZoomOut() |
|
} |
|
} |
|
|
|
@IBAction func closeButtonPressed(_ sender: Any) { |
|
dismiss(animated: true) |
|
} |
|
|
|
@IBAction func downloadPressed(_ sender: Any) { |
|
guard let image = image else { return } |
|
PHPhotoLibrary.shared().performChanges({ |
|
PHAssetChangeRequest.creationRequestForAsset(from: image) |
|
}, completionHandler: { success, error in |
|
if success { |
|
return |
|
} else if let error = error { |
|
print("Couldn't save photo: \(error)") |
|
} else { |
|
return |
|
} |
|
}) |
|
} |
|
|
|
}
|
|
|