Verified Commit 3022739e authored by Geoff Pado's avatar Geoff Pado

Zoom image view to correct scale to match observations

parent 2bf5cce3
......@@ -34,6 +34,8 @@
043CD9CB226EB0830012F5AE /* TextRectangleDetectionOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 043CD9CA226EB0830012F5AE /* TextRectangleDetectionOperation.swift */; };
043CD9CF226EB0C70012F5AE /* DetectedTextObservation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 043CD9CE226EB0C70012F5AE /* DetectedTextObservation.swift */; };
043CD9D1226EB0F60012F5AE /* UIImageExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 043CD9D0226EB0F60012F5AE /* UIImageExtensions.swift */; };
043CD9D62275316E0012F5AE /* PhotoEditingScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 043CD9D52275316E0012F5AE /* PhotoEditingScrollView.swift */; };
043CD9D8227531DF0012F5AE /* PhotoEditingObservationVisualizationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 043CD9D7227531DF0012F5AE /* PhotoEditingObservationVisualizationView.swift */; };
047072932268134500FF20B6 /* PhotoEditingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 047072922268134500FF20B6 /* PhotoEditingView.swift */; };
047072952268137900FF20B6 /* PhotoEditingImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 047072942268137900FF20B6 /* PhotoEditingImageView.swift */; };
048909F2226571AB0048E203 /* PhotoEditingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048909F1226571AB0048E203 /* PhotoEditingViewController.swift */; };
......@@ -88,6 +90,8 @@
043CD9CA226EB0830012F5AE /* TextRectangleDetectionOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRectangleDetectionOperation.swift; sourceTree = "<group>"; };
043CD9CE226EB0C70012F5AE /* DetectedTextObservation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetectedTextObservation.swift; sourceTree = "<group>"; };
043CD9D0226EB0F60012F5AE /* UIImageExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImageExtensions.swift; sourceTree = "<group>"; };
043CD9D52275316E0012F5AE /* PhotoEditingScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoEditingScrollView.swift; sourceTree = "<group>"; };
043CD9D7227531DF0012F5AE /* PhotoEditingObservationVisualizationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoEditingObservationVisualizationView.swift; sourceTree = "<group>"; };
047072922268134500FF20B6 /* PhotoEditingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoEditingView.swift; sourceTree = "<group>"; };
047072942268137900FF20B6 /* PhotoEditingImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoEditingImageView.swift; sourceTree = "<group>"; };
048909F1226571AB0048E203 /* PhotoEditingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoEditingViewController.swift; sourceTree = "<group>"; };
......@@ -223,6 +227,8 @@
047072922268134500FF20B6 /* PhotoEditingView.swift */,
047072942268137900FF20B6 /* PhotoEditingImageView.swift */,
048909F3226573750048E203 /* PhotoEditorPresenting.swift */,
043CD9D52275316E0012F5AE /* PhotoEditingScrollView.swift */,
043CD9D7227531DF0012F5AE /* PhotoEditingObservationVisualizationView.swift */,
);
path = "Photo Editing";
sourceTree = "<group>";
......@@ -396,6 +402,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
043CD9D8227531DF0012F5AE /* PhotoEditingObservationVisualizationView.swift in Sources */,
041EFF5D2255A85F0058D8EE /* NavigationController.swift in Sources */,
041EFF492252FAF50058D8EE /* IntroViewController.swift in Sources */,
047072952268137900FF20B6 /* PhotoEditingImageView.swift in Sources */,
......@@ -411,6 +418,7 @@
041EFEFB2251A9F30058D8EE /* AppViewController.swift in Sources */,
041EFF6E225C38800058D8EE /* PhotoSelectionViewController.swift in Sources */,
04D68BCC2262B35700D09BBD /* PhotoPermissionsDeniedAlertController.swift in Sources */,
043CD9D62275316E0012F5AE /* PhotoEditingScrollView.swift in Sources */,
04D68BC92262B0C400D09BBD /* PhotoPermissionsRequester.swift in Sources */,
041EFF192251AAFE0058D8EE /* UIViewControllerExtensions.swift in Sources */,
041EFF52225303430058D8EE /* PromptButton.swift in Sources */,
......
......@@ -8,3 +8,29 @@ extension CGSize {
return CGSize(width: size.width * multiplier, height: size.height * multiplier)
}
}
extension CGRect {
func fitting(rect fittingRect: CGRect) -> CGRect {
let aspectRatio = width / height
let fittingAspectRatio = fittingRect.width / fittingRect.height
if fittingAspectRatio > aspectRatio { //wider fitting rect
let newRectWidth = aspectRatio * fittingRect.height
let newRectHeight = fittingRect.height
let newRectX = (fittingRect.width - newRectWidth) / 2
let newRectY = CGFloat(0)
return CGRect(x: newRectX, y: newRectY, width: newRectWidth, height: newRectHeight)
} else if fittingAspectRatio < aspectRatio { //taller fitting rect
let newRectWidth = fittingRect.width
let newRectHeight = 1 / (aspectRatio / fittingRect.width)
let newRectX = CGFloat(0)
let newRectY = (fittingRect.height - newRectHeight) / 2
return CGRect(x: newRectX, y: newRectY, width: newRectWidth, height: newRectHeight)
} else { //same aspect ratio
return fittingRect
}
}
}
......@@ -7,7 +7,7 @@ class PhotoEditingImageView: UIImageView {
init() {
super.init(frame: .zero)
translatesAutoresizingMaskIntoConstraints = false
contentMode = .scaleAspectFit
contentMode = .center
}
@available(*, unavailable)
......
// Created by Geoff Pado on 4/27/19.
// Copyright © 2019 Cocoatype, LLC. All rights reserved.
import UIKit
class PhotoEditingObservationVisualizationView: UIView {
init() {
super.init(frame: .zero)
backgroundColor = .clear
isOpaque = false
translatesAutoresizingMaskIntoConstraints = false
}
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let textObservations = textObservations else { return }
UIColor.black.setFill()
textObservations.forEach { observation in
UIBezierPath(rect: observation.bounds).fill()
}
}
var textObservations: [DetectedTextObservation]? {
didSet {
setNeedsDisplay()
}
}
// MARK: Boilerplate
@available(*, unavailable)
required init(coder: NSCoder) {
let className = String(describing: type(of: self))
fatalError("\(className) does not implement init(coder:)")
}
}
// Created by Geoff Pado on 4/27/19.
// Copyright © 2019 Cocoatype, LLC. All rights reserved.
import UIKit
class PhotoEditingScrollView: UIScrollView {
init() {
photoEditingView = PhotoEditingView()
super.init(frame: .zero)
backgroundColor = .primary
addSubview(photoEditingView)
NSLayoutConstraint.activate([
photoEditingView.centerXAnchor.constraint(equalTo: contentLayoutGuide.centerXAnchor),
photoEditingView.centerYAnchor.constraint(equalTo: contentLayoutGuide.centerYAnchor),
photoEditingView.widthAnchor.constraint(equalTo: contentLayoutGuide.widthAnchor),
photoEditingView.heightAnchor.constraint(equalTo: contentLayoutGuide.heightAnchor)
])
}
private(set) var photoEditingView: PhotoEditingView
var image: UIImage? {
get { return photoEditingView.image }
set(newImage) {
photoEditingView.image = newImage
updateZoomScale()
}
}
var textObservations: [DetectedTextObservation]? {
get { return photoEditingView.textObservations }
set(newTextObservations) {
photoEditingView.textObservations = newTextObservations
}
}
// MARK: Scaling
private var minimumZoomScaleForCurrentImage: CGFloat {
guard let image = image else { return 1.0 }
let imageSize = image.size * image.scale
let imageBounds = CGRect(origin: .zero, size: imageSize)
let zoomedBounds = imageBounds.fitting(rect: bounds).integral
return zoomedBounds.width / imageBounds.width
}
private func updateZoomScale() {
minimumZoomScale = minimumZoomScaleForCurrentImage
if zoomScale == 1.0 {
zoomScale = minimumZoomScaleForCurrentImage
}
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
updateZoomScale()
}
// MARK: Boilerplate
@available(*, unavailable)
required init(coder: NSCoder) {
let className = String(describing: type(of: self))
fatalError("\(className) does not implement init(coder:)")
}
}
......@@ -6,10 +6,11 @@ import UIKit
class PhotoEditingView: UIView {
init() {
imageView = PhotoEditingImageView()
visualizationView = DetectionVisualizationView()
visualizationView = PhotoEditingObservationVisualizationView()
super.init(frame: .zero)
backgroundColor = .primary
translatesAutoresizingMaskIntoConstraints = false
addSubview(imageView)
addSubview(visualizationView)
......@@ -43,42 +44,7 @@ class PhotoEditingView: UIView {
// MARK: Boilerplate
private var imageView: PhotoEditingImageView
private var visualizationView: DetectionVisualizationView
@available(*, unavailable)
required init(coder: NSCoder) {
let className = String(describing: type(of: self))
fatalError("\(className) does not implement init(coder:)")
}
}
class DetectionVisualizationView: UIView {
init() {
super.init(frame: .zero)
backgroundColor = .clear
isOpaque = false
translatesAutoresizingMaskIntoConstraints = false
}
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let textObservations = textObservations else { return }
UIColor.black.setFill()
textObservations.forEach { observation in
UIBezierPath(rect: observation.bounds).fill()
}
}
var textObservations: [DetectedTextObservation]? {
didSet {
setNeedsDisplay()
}
}
// MARK: Boilerplate
private var visualizationView: PhotoEditingObservationVisualizationView
@available(*, unavailable)
required init(coder: NSCoder) {
......
......@@ -4,7 +4,7 @@
import Photos
import UIKit
class PhotoEditingViewController: UIViewController {
class PhotoEditingViewController: UIViewController, UIScrollViewDelegate {
init(asset: PHAsset) {
self.asset = asset
super.init(nibName: nil, bundle: nil)
......@@ -13,7 +13,8 @@ class PhotoEditingViewController: UIViewController {
}
override func loadView() {
view = PhotoEditingView()
view = PhotoEditingScrollView()
photoScrollView?.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
......@@ -30,22 +31,28 @@ class PhotoEditingViewController: UIViewController {
self?.textRectangleDetector.detectTextRectangles(in: image) { (textObservations) in
DispatchQueue.main.async { [weak self] in
self?.photoEditingView?.textObservations = textObservations
self?.photoScrollView?.textObservations = textObservations
}
}
DispatchQueue.main.async { [weak self] in
self?.photoEditingView?.image = image
self?.photoScrollView?.image = image
}
}
}
// MARK: UIScrollViewDelegate
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return photoScrollView?.photoEditingView
}
// MARK: Boilerplate
private let asset: PHAsset
private let imageManager = PHImageManager()
private var photoEditingView: PhotoEditingView? { return view as? PhotoEditingView }
private let textRectangleDetector = TextRectangleDetector()
private var photoScrollView: PhotoEditingScrollView? { return (view as? PhotoEditingScrollView) }
@available(*, unavailable)
required init(coder: NSCoder) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment