Tusker/Tusker/Screens/Large Image/LargeImageViewController.swift

230 lines
8.5 KiB
Swift
Raw Normal View History

2018-09-02 20:59:20 +00:00
//
// LargeImageViewController.swift
// Tusker
//
// Created by Shadowfacts on 8/31/18.
// Copyright © 2018 Shadowfacts. All rights reserved.
//
import UIKit
import Photos
protocol LargeImageViewControllerDelegate {
func closeLargeImage()
}
class LargeImageViewController: UIViewController, UIScrollViewDelegate {
2018-10-12 01:20:58 +00:00
static func create(image: UIImage, description: String?, sourceView: UIView, sourceViewController: UIViewController) -> LargeImageViewController {
2018-09-02 20:59:20 +00:00
guard let vc = UIStoryboard(name: "LargeImage", bundle: nil).instantiateInitialViewController() as? LargeImageViewController else { fatalError() }
vc.image = image
vc.imageDescription = description
2018-10-12 01:20:58 +00:00
var frame = sourceView.convert(sourceView.bounds, to: sourceViewController.view)
if let scrollView = sourceViewController.view as? UIScrollView {
let scale = scrollView.zoomScale
let width = frame.width * scale
let height = frame.height * scale
let x = frame.minX * scale - scrollView.contentOffset.x + scrollView.frame.minX
let y = frame.minY * scale - scrollView.contentOffset.y + scrollView.frame.minY
frame = CGRect(x: x, y: y, width: width, height: height)
}
vc.originFrame = frame
vc.originCornerRadius = sourceView.layer.cornerRadius
vc.transitioningDelegate = sourceViewController
2018-09-02 20:59:20 +00:00
return vc
}
var delegate: LargeImageViewControllerDelegate?
var originFrame: CGRect?
var originCornerRadius: CGFloat?
2018-09-02 20:59:20 +00:00
var dismissInteractionController: LargeImageInteractionController?
@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var imageView: UIImageView!
@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
2018-09-02 20:59:20 +00:00
var image: UIImage?
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
}
override func viewDidLoad() {
super.viewDidLoad()
imageView.image = image
scrollView.delegate = self
imageView.bounds = CGRect(origin: .zero, size: image!.size)
if let imageDescription = imageDescription {
descriptionLabel.text = imageDescription
} else {
bottomControlsView.isHidden = true
}
dismissInteractionController = LargeImageInteractionController(viewController: self)
}
2018-09-02 20:59:20 +00:00
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
2018-09-02 20:59:20 +00:00
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
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
}
}
2018-09-02 20:59:20 +00:00
}
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()
})
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
@IBAction func scrollViewPressed(_ sender: UITapGestureRecognizer) {
if scrollView.zoomScale > scrollView.minimumZoomScale {
animateZoomOut()
} else {
controlsVisible = !controlsVisible
}
}
@IBAction 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) {
delegate?.closeLargeImage()
}
@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
}
})
}
}