From 9bb976a7c4f80db4223beb263f383a7e5f62d7c9 Mon Sep 17 00:00:00 2001 From: Evgeniy Safronov Date: Tue, 12 Sep 2017 17:57:02 +0300 Subject: [PATCH] Added rectangular and square cropping frame support. --- .../Utilities/CroppingParameters.swift | 16 ++-- .../ConfirmViewController.swift | 4 +- .../Views/CropOverlay.swift | 85 ++++++++++++++----- Example/ViewController.swift | 3 +- 4 files changed, 79 insertions(+), 29 deletions(-) diff --git a/ALCameraViewController/Utilities/CroppingParameters.swift b/ALCameraViewController/Utilities/CroppingParameters.swift index 88f90acf..9e1d7462 100644 --- a/ALCameraViewController/Utilities/CroppingParameters.swift +++ b/ALCameraViewController/Utilities/CroppingParameters.swift @@ -9,14 +9,20 @@ import UIKit public struct CroppingParameters { + + public enum ResizingMode { + case none + case rectangle + case square + } /// Enable the cropping feature. /// Default value is set to false. var isEnabled: Bool - /// Allow the cropping area to be resized by the user. - /// Default value is set to true. - var allowResizing: Bool + /// Select resizing mode for the cropping frame. + /// Use .rectangle for free form resizing, .square for square resizing and .none if you want to disable resizing + var resizingMode: ResizingMode /// Allow the cropping area to be moved by the user. /// Default value is set to false. @@ -27,12 +33,12 @@ public struct CroppingParameters { var minimumSize: CGSize public init(isEnabled: Bool = false, - allowResizing: Bool = true, + resizingMode: ResizingMode = .rectangle, allowMoving: Bool = true, minimumSize: CGSize = CGSize(width: 60, height: 60)) { self.isEnabled = isEnabled - self.allowResizing = allowResizing + self.resizingMode = resizingMode self.allowMoving = allowMoving self.minimumSize = minimumSize } diff --git a/ALCameraViewController/ViewController/ConfirmViewController.swift b/ALCameraViewController/ViewController/ConfirmViewController.swift index d9eccd23..a2147bbc 100644 --- a/ALCameraViewController/ViewController/ConfirmViewController.swift +++ b/ALCameraViewController/ViewController/ConfirmViewController.swift @@ -20,7 +20,7 @@ public class ConfirmViewController: UIViewController, UIScrollViewDelegate { var croppingParameters: CroppingParameters { didSet { - cropOverlay.isResizable = croppingParameters.allowResizing + cropOverlay.resizingMode = croppingParameters.resizingMode cropOverlay.minimumSize = croppingParameters.minimumSize } } @@ -69,7 +69,7 @@ public class ConfirmViewController: UIViewController, UIScrollViewDelegate { scrollView.maximumZoomScale = 1 cropOverlay.isHidden = true - cropOverlay.isResizable = croppingParameters.allowResizing + cropOverlay.resizingMode = croppingParameters.resizingMode cropOverlay.isMovable = croppingParameters.allowMoving cropOverlay.minimumSize = croppingParameters.minimumSize diff --git a/ALCameraViewController/Views/CropOverlay.swift b/ALCameraViewController/Views/CropOverlay.swift index 626c1e3b..ac656149 100644 --- a/ALCameraViewController/Views/CropOverlay.swift +++ b/ALCameraViewController/Views/CropOverlay.swift @@ -34,7 +34,7 @@ internal class CropOverlay: UIView { return self.cornerButtonWidth * self.outterGapRatio } - var isResizable: Bool = false + var resizingMode: CroppingParameters.ResizingMode = .none var isMovable: Bool = false var minimumSize: CGSize = CGSize.zero @@ -130,7 +130,6 @@ internal class CropOverlay: UIView { } func createLines() { - outerLines = [createLine(), createLine(), createLine(), createLine()] horizontalLines = [createLine(), createLine()] verticalLines = [createLine(), createLine()] @@ -165,27 +164,21 @@ internal class CropOverlay: UIView { } func moveCropOverlay(gestureRecognizer: UIPanGestureRecognizer) { - if isResizable, let button = gestureRecognizer.view as? UIButton { + if resizingMode != .none, let button = gestureRecognizer.view as? UIButton { if gestureRecognizer.state == .began || gestureRecognizer.state == .changed { - let translation = gestureRecognizer.translation(in: self) - - var newFrame: CGRect - - switch button { - case cornerButtons[0]: // Top Left - newFrame = CGRect(x: frame.origin.x + translation.x, y: frame.origin.y + translation.y, width: frame.size.width - translation.x, height: frame.size.height - translation.y) - case cornerButtons[1]: // Top Right - newFrame = CGRect(x: frame.origin.x, y: frame.origin.y + translation.y, width: frame.size.width + translation.x, height: frame.size.height - translation.y) - case cornerButtons[2]: // Bottom Left - newFrame = CGRect(x: frame.origin.x + translation.x, y: frame.origin.y, width: frame.size.width - translation.x, height: frame.size.height + translation.y) - case cornerButtons[3]: // Bottom Right - newFrame = CGRect(x: frame.origin.x, y: frame.origin.y, width: frame.size.width + translation.x, height: frame.size.height + translation.y) - default: - newFrame = CGRect.zero - } + let translation = gestureRecognizer.translation(in: self) + + let newFrame: CGRect + if resizingMode == .rectangle { + newFrame = getNewRectangleFrame(translation: translation, button: button) + } else { + newFrame = getNewSquareFrame(translation: translation, button: button) + } let minimumFrame = CGRect(x: newFrame.origin.x, y: newFrame.origin.y, width: max(newFrame.size.width, minimumSize.width + 2 * outterGap), height: max(newFrame.size.height, minimumSize.height + 2 * outterGap)) - frame = minimumFrame + + frame = minimumFrame + layoutSubviews() gestureRecognizer.setTranslation(CGPoint.zero, in: self) @@ -199,11 +192,61 @@ internal class CropOverlay: UIView { } } } + + private func getNewRectangleFrame(translation: CGPoint, button: UIButton) -> CGRect { + switch button { + case cornerButtons[0]: // Top Left + return CGRect(x: frame.origin.x + translation.x, y: frame.origin.y + translation.y, width: frame.size.width - translation.x, height: frame.size.height - translation.y) + case cornerButtons[1]: // Top Right + return CGRect(x: frame.origin.x, y: frame.origin.y + translation.y, width: frame.size.width + translation.x, height: frame.size.height - translation.y) + case cornerButtons[2]: // Bottom Left + return CGRect(x: frame.origin.x + translation.x, y: frame.origin.y, width: frame.size.width - translation.x, height: frame.size.height + translation.y) + case cornerButtons[3]: // Bottom Right + return CGRect(x: frame.origin.x, y: frame.origin.y, width: frame.size.width + translation.x, height: frame.size.height + translation.y) + default: + return CGRect.zero + } + } + + private func getNewSquareFrame(translation: CGPoint, button: UIButton) -> CGRect { + let distance = max(abs(translation.x), abs(translation.y)) + let dx = translation.x < 0 ? -distance : distance + let dy = translation.y < 0 ? -distance : distance + + switch button { + case cornerButtons[0]: // Top Left + guard dx>0 && dy>0 || dx<0 && dy<0 else { + return frame + } + + return CGRect(x: frame.origin.x + dx, y: frame.origin.y + dy, width: frame.size.width - dx, height: frame.size.height - dy) + case cornerButtons[1]: // Top Right + guard dx>0 && dy<0 || dx<0 && dy>0 else { + return frame + } + + return CGRect(x: frame.origin.x, y: frame.origin.y + dy, width: frame.size.width + dx, height: frame.size.height - dy) + case cornerButtons[2]: // Bottom Left + guard dx<0 && dy>0 || dx>0 && dy<0 else { + return frame + } + + return CGRect(x: frame.origin.x + dx, y: frame.origin.y, width: frame.size.width - dx, height: frame.size.height + dy) + case cornerButtons[3]: // Bottom Right + guard dx>0 && dy>0 || dx<0 && dy<0 else { + return frame + } + + return CGRect(x: frame.origin.x, y: frame.origin.y, width: frame.size.width + dx, height: frame.size.height + dy) + default: + return CGRect.zero + } + } override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { let view = super.hitTest(point, with: event) - if !isMovable && isResizable && view != nil { + if !isMovable && resizingMode != .none && view != nil { let isButton = cornerButtons.reduce(false) { $1.hitTest(convert(point, to: $1), with: event) != nil || $0 } if !isButton { return nil diff --git a/Example/ViewController.swift b/Example/ViewController.swift index e4c6aa1b..3ec3abe5 100644 --- a/Example/ViewController.swift +++ b/Example/ViewController.swift @@ -17,7 +17,8 @@ class ViewController: UIViewController { var minimumSize: CGSize = CGSize(width: 60, height: 60) var croppingParameters: CroppingParameters { - return CroppingParameters(isEnabled: croppingEnabled, allowResizing: allowResizing, allowMoving: allowMoving, minimumSize: minimumSize) + let resizingMode: CroppingParameters.ResizingMode = allowResizing ? .square : .none + return CroppingParameters(isEnabled: croppingEnabled, resizingMode: resizingMode, allowMoving: allowMoving, minimumSize: minimumSize) } @IBOutlet weak var imageView: UIImageView!