From 4a80d04d3c560018f5e3da43412c7cab4f893173 Mon Sep 17 00:00:00 2001 From: Guillaume Bellut Date: Sat, 2 Sep 2017 22:41:22 +0200 Subject: [PATCH 1/4] Added resizable cropping area feature. --- .../project.pbxproj | 4 + .../Utilities/CroppingParameters.swift | 33 ++++ .../Utilities/SingleImageFetcher.swift | 9 +- .../ViewController/CameraViewController.swift | 27 +-- .../ConfirmViewController.swift | 61 ++++--- .../ViewController/ConfirmViewController.xib | 53 +++--- .../Views/CropOverlay.swift | 161 +++++++++++++----- Example/ViewController.swift | 52 +++++- Example/ViewController.xib | 74 +++++++- 9 files changed, 347 insertions(+), 127 deletions(-) create mode 100644 ALCameraViewController/Utilities/CroppingParameters.swift diff --git a/ALCameraViewController.xcodeproj/project.pbxproj b/ALCameraViewController.xcodeproj/project.pbxproj index 947182ed..1d0b5fa9 100644 --- a/ALCameraViewController.xcodeproj/project.pbxproj +++ b/ALCameraViewController.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 7AC96FA21F5B5166003E53F4 /* CroppingParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC96FA11F5B5166003E53F4 /* CroppingParameters.swift */; }; C40665441C73A47C00EB9751 /* SingleImageSaver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40665431C73A47C00EB9751 /* SingleImageSaver.swift */; }; C40665461C73A94100EB9751 /* CameraGlobals.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40665451C73A94100EB9751 /* CameraGlobals.swift */; }; C40665481C73B72D00EB9751 /* SingleImageFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40665471C73B72D00EB9751 /* SingleImageFetcher.swift */; }; @@ -58,6 +59,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 7AC96FA11F5B5166003E53F4 /* CroppingParameters.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CroppingParameters.swift; sourceTree = ""; }; C40665431C73A47C00EB9751 /* SingleImageSaver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SingleImageSaver.swift; sourceTree = ""; }; C40665451C73A94100EB9751 /* CameraGlobals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraGlobals.swift; sourceTree = ""; }; C40665471C73B72D00EB9751 /* SingleImageFetcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SingleImageFetcher.swift; sourceTree = ""; }; @@ -154,6 +156,7 @@ C4D9BA441CA7224B004F70F7 /* PhotoLibraryAuthorizer.swift */, C4D9BA461CA73163004F70F7 /* UIButtonExtensions.swift */, EBFE097C1CAF1D1A00A8C637 /* UIViewExtensions.swift */, + 7AC96FA11F5B5166003E53F4 /* CroppingParameters.swift */, ); path = Utilities; sourceTree = ""; @@ -366,6 +369,7 @@ FAB50C001B413E8C009905B9 /* PhotoLibraryViewController.swift in Sources */, FAF0586A1B317894008E5592 /* CameraShot.swift in Sources */, FAF058661B316695008E5592 /* CameraViewController.swift in Sources */, + 7AC96FA21F5B5166003E53F4 /* CroppingParameters.swift in Sources */, C4D9BA451CA7224B004F70F7 /* PhotoLibraryAuthorizer.swift in Sources */, FAB50BFB1B413E8C009905B9 /* ImageCell.swift in Sources */, EBFE097D1CAF1D1A00A8C637 /* UIViewExtensions.swift in Sources */, diff --git a/ALCameraViewController/Utilities/CroppingParameters.swift b/ALCameraViewController/Utilities/CroppingParameters.swift new file mode 100644 index 00000000..fdc31d9c --- /dev/null +++ b/ALCameraViewController/Utilities/CroppingParameters.swift @@ -0,0 +1,33 @@ +// +// CroppingParameters.swift +// ALCameraViewController +// +// Created by Guillaume Bellut on 02/09/2017. +// Copyright © 2017 zero. All rights reserved. +// + +import UIKit + +public struct CroppingParameters { + + /// 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 + + /// Prevent the user to resize the cropping area below a minimum size. + /// Default value is (60, 60). Below this value, corner buttons will overlap. + var minimumSize: CGSize + + init(isEnabled: Bool = false, + allowResizing: Bool = true, + minimumSize: CGSize = CGSize(width: 60, height: 60)) { + + self.isEnabled = isEnabled + self.allowResizing = allowResizing + self.minimumSize = minimumSize + } +} diff --git a/ALCameraViewController/Utilities/SingleImageFetcher.swift b/ALCameraViewController/Utilities/SingleImageFetcher.swift index 9e266758..dbbb9c6a 100644 --- a/ALCameraViewController/Utilities/SingleImageFetcher.swift +++ b/ALCameraViewController/Utilities/SingleImageFetcher.swift @@ -78,12 +78,11 @@ public class SingleImageFetcher { options.resizeMode = .exact let targetWidth = floor(CGFloat(asset.pixelWidth) * cropRect.width) - let targetHeight = floor(CGFloat(asset.pixelHeight) * cropRect.height) - let dimension = max(min(targetHeight, targetWidth), 1024 * scale) - - targetSize = CGSize(width: dimension, height: dimension) + let targetHeight = floor(CGFloat(asset.pixelHeight) * cropRect.height) + + targetSize = CGSize(width: targetWidth, height: targetHeight) } - + PHImageManager.default().requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFill, options: options) { image, _ in if let image = image { self.success?(image) diff --git a/ALCameraViewController/ViewController/CameraViewController.swift b/ALCameraViewController/ViewController/CameraViewController.swift index a1adc0e8..f23d25bc 100644 --- a/ALCameraViewController/ViewController/CameraViewController.swift +++ b/ALCameraViewController/ViewController/CameraViewController.swift @@ -14,7 +14,7 @@ public typealias CameraViewCompletion = (UIImage?, PHAsset?) -> Void public extension CameraViewController { /// Provides an image picker wrapped inside a UINavigationController instance - public class func imagePickerViewController(croppingEnabled: Bool, completion: @escaping CameraViewCompletion) -> UINavigationController { + public class func imagePickerViewController(croppingParameters: CroppingParameters, completion: @escaping CameraViewCompletion) -> UINavigationController { let imagePicker = PhotoLibraryViewController() let navigationController = UINavigationController(rootViewController: imagePicker) @@ -24,7 +24,7 @@ public extension CameraViewController { imagePicker.onSelectionComplete = { [weak imagePicker] asset in if let asset = asset { - let confirmController = ConfirmViewController(asset: asset, allowsCropping: croppingEnabled) + let confirmController = ConfirmViewController(asset: asset, croppingParameters: croppingParameters) confirmController.onComplete = { [weak imagePicker] image, asset in if let image = image, let asset = asset { completion(image, asset) @@ -46,7 +46,7 @@ public extension CameraViewController { open class CameraViewController: UIViewController { var didUpdateViews = false - var allowCropping = false + var croppingParameters: CroppingParameters var animationRunning = false let allowVolumeButtonCapture: Bool @@ -89,7 +89,7 @@ open class CameraViewController: UIViewController { cameraView.translatesAutoresizingMaskIntoConstraints = false return cameraView }() - + let cameraOverlay : CropOverlay = { let cameraOverlay = CropOverlay() cameraOverlay.translatesAutoresizingMaskIntoConstraints = false @@ -159,13 +159,18 @@ open class CameraViewController: UIViewController { private let allowsLibraryAccess: Bool - public init(croppingEnabled: Bool, allowsLibraryAccess: Bool = true, allowsSwapCameraOrientation: Bool = true, allowVolumeButtonCapture: Bool = true, completion: @escaping CameraViewCompletion) { - self.allowsLibraryAccess = allowsLibraryAccess + public init(croppingParameters: CroppingParameters = CroppingParameters(), + allowsLibraryAccess: Bool = true, + allowsSwapCameraOrientation: Bool = true, + allowVolumeButtonCapture: Bool = true, + completion: @escaping CameraViewCompletion) { + + self.croppingParameters = croppingParameters + self.allowsLibraryAccess = allowsLibraryAccess self.allowVolumeButtonCapture = allowVolumeButtonCapture super.init(nibName: nil, bundle: nil) onCompletion = completion - allowCropping = croppingEnabled - cameraOverlay.isHidden = !allowCropping + cameraOverlay.isHidden = !croppingParameters.isEnabled libraryButton.isEnabled = allowsLibraryAccess libraryButton.isHidden = !allowsLibraryAccess swapButton.isEnabled = allowsSwapCameraOrientation @@ -539,7 +544,7 @@ open class CameraViewController: UIViewController { } internal func showLibrary() { - let imagePicker = CameraViewController.imagePickerViewController(croppingEnabled: allowCropping) { [weak self] image, asset in + let imagePicker = CameraViewController.imagePickerViewController(croppingParameters: croppingParameters) { [weak self] image, asset in defer { self?.dismiss(animated: true, completion: nil) } @@ -588,7 +593,7 @@ open class CameraViewController: UIViewController { } private func startConfirmController(uiImage: UIImage) { - let confirmViewController = ConfirmViewController(image: uiImage, allowsCropping: allowCropping) + let confirmViewController = ConfirmViewController(image: uiImage, croppingParameters: croppingParameters) confirmViewController.onComplete = { [weak self] image, asset in defer { self?.dismiss(animated: true, completion: nil) @@ -606,7 +611,7 @@ open class CameraViewController: UIViewController { } private func startConfirmController(asset: PHAsset) { - let confirmViewController = ConfirmViewController(asset: asset, allowsCropping: allowCropping) + let confirmViewController = ConfirmViewController(asset: asset, croppingParameters: croppingParameters) confirmViewController.onComplete = { [weak self] image, asset in defer { self?.dismiss(animated: true, completion: nil) diff --git a/ALCameraViewController/ViewController/ConfirmViewController.swift b/ALCameraViewController/ViewController/ConfirmViewController.swift index dba22b06..0a540225 100644 --- a/ALCameraViewController/ViewController/ConfirmViewController.swift +++ b/ALCameraViewController/ViewController/ConfirmViewController.swift @@ -18,33 +18,37 @@ public class ConfirmViewController: UIViewController, UIScrollViewDelegate { @IBOutlet weak var confirmButton: UIButton! @IBOutlet weak var centeringView: UIView! - var allowsCropping: Bool = false + var croppingParameters: CroppingParameters { + didSet { + cropOverlay.isResizable = croppingParameters.allowResizing + cropOverlay.minimumSize = croppingParameters.minimumSize + } + } + var verticalPadding: CGFloat = 30 var horizontalPadding: CGFloat = 30 public var onComplete: CameraViewCompletion? - + let asset: PHAsset? let image: UIImage? - public init(image: UIImage, allowsCropping: Bool) { - self.allowsCropping = allowsCropping + public init(image: UIImage, croppingParameters: CroppingParameters) { + self.croppingParameters = croppingParameters self.asset = nil self.image = image super.init(nibName: "ConfirmViewController", bundle: CameraGlobals.shared.bundle) } - public init(asset: PHAsset, allowsCropping: Bool) { - self.allowsCropping = allowsCropping + public init(asset: PHAsset, croppingParameters: CroppingParameters) { + self.croppingParameters = croppingParameters self.asset = asset self.image = nil super.init(nibName: "ConfirmViewController", bundle: CameraGlobals.shared.bundle) } - public required init?(coder aDecoder: NSCoder) { - asset = nil - image = nil - super.init(coder: aDecoder) + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") } public override var prefersStatusBarHidden: Bool { @@ -57,15 +61,17 @@ public class ConfirmViewController: UIViewController, UIScrollViewDelegate { public override func viewDidLoad() { super.viewDidLoad() - + view.backgroundColor = UIColor.black scrollView.addSubview(imageView) scrollView.delegate = self scrollView.maximumZoomScale = 1 - cropOverlay.isHidden = true - + cropOverlay.isHidden = true + cropOverlay.isResizable = croppingParameters.allowResizing + cropOverlay.minimumSize = croppingParameters.minimumSize + let spinner = showSpinner() disable() @@ -93,7 +99,7 @@ public class ConfirmViewController: UIViewController, UIScrollViewDelegate { public override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() let scale = calculateMinimumScale(view.frame.size) - let frame = allowsCropping ? cropOverlay.frame : view.bounds + let frame = croppingParameters.isEnabled ? cropOverlay.frame : view.bounds scrollView.contentInset = calculateScrollViewInsets(frame) scrollView.minimumZoomScale = scale @@ -108,7 +114,7 @@ public class ConfirmViewController: UIViewController, UIScrollViewDelegate { let scale = calculateMinimumScale(size) var frame = view.bounds - if allowsCropping { + if croppingParameters.isEnabled { frame = cropOverlay.frame let centeringFrame = centeringView.frame var origin: CGPoint @@ -139,11 +145,7 @@ public class ConfirmViewController: UIViewController, UIScrollViewDelegate { } private func configureWithImage(_ image: UIImage) { - if allowsCropping { - cropOverlay.isHidden = false - } else { - cropOverlay.isHidden = true - } + cropOverlay.isHidden = !croppingParameters.isEnabled buttonActions() @@ -155,7 +157,7 @@ public class ConfirmViewController: UIViewController, UIScrollViewDelegate { private func calculateMinimumScale(_ size: CGSize) -> CGFloat { var _size = size - if allowsCropping { + if croppingParameters.isEnabled { _size = cropOverlay.frame.size } @@ -168,7 +170,7 @@ public class ConfirmViewController: UIViewController, UIScrollViewDelegate { var scale: CGFloat - if allowsCropping { + if croppingParameters.isEnabled { scale = max(scaleWidth, scaleHeight) } else { scale = min(scaleWidth, scaleHeight) @@ -185,8 +187,8 @@ public class ConfirmViewController: UIViewController, UIScrollViewDelegate { } private func centerImageViewOnRotate() { - if allowsCropping { - let size = allowsCropping ? cropOverlay.frame.size : scrollView.frame.size + if croppingParameters.isEnabled { + let size = cropOverlay.frame.size let scrollInsets = scrollView.contentInset let imageSize = imageView.frame.size var contentOffset = CGPoint(x: -scrollInsets.left, y: -scrollInsets.top) @@ -197,7 +199,7 @@ public class ConfirmViewController: UIViewController, UIScrollViewDelegate { } private func centerScrollViewContents() { - let size = allowsCropping ? cropOverlay.frame.size : scrollView.frame.size + let size = croppingParameters.isEnabled ? cropOverlay.frame.size : scrollView.frame.size let imageSize = imageView.frame.size var imageOrigin = CGPoint.zero @@ -245,7 +247,7 @@ public class ConfirmViewController: UIViewController, UIScrollViewDelegate { self?.showNoImageScreen(error) } .setAsset(asset) - if allowsCropping { + if croppingParameters.isEnabled { let rect = normalizedRect(makeProportionalCropRect(), orientation: image.imageOrientation) fetcher = fetcher.setCropRect(rect) } @@ -254,7 +256,7 @@ public class ConfirmViewController: UIViewController, UIScrollViewDelegate { } else { var newImage = image - if allowsCropping { + if croppingParameters.isEnabled { let cropRect = makeProportionalCropRect() let resizedCropRect = CGRect(x: (image.size.width) * cropRect.origin.x, y: (image.size.height) * cropRect.origin.y, @@ -311,7 +313,10 @@ public class ConfirmViewController: UIViewController, UIScrollViewDelegate { } private func makeProportionalCropRect() -> CGRect { - var cropRect = cropOverlay.frame + var cropRect = CGRect(x: cropOverlay.frame.origin.x + cropOverlay.outterGap, + y: cropOverlay.frame.origin.y + cropOverlay.outterGap, + width: cropOverlay.frame.size.width - 2 * cropOverlay.outterGap, + height: cropOverlay.frame.size.height - 2 * cropOverlay.outterGap) cropRect.origin.x += scrollView.contentOffset.x cropRect.origin.y += scrollView.contentOffset.y diff --git a/ALCameraViewController/ViewController/ConfirmViewController.xib b/ALCameraViewController/ViewController/ConfirmViewController.xib index fd8a355a..c3f3a9dc 100644 --- a/ALCameraViewController/ViewController/ConfirmViewController.xib +++ b/ALCameraViewController/ViewController/ConfirmViewController.xib @@ -1,8 +1,13 @@ - - + + + + + - + + + @@ -17,47 +22,41 @@ - + - - - - - - - + - - - - - - - - - - - + diff --git a/ALCameraViewController/Views/CropOverlay.swift b/ALCameraViewController/Views/CropOverlay.swift index e330223f..a5192df8 100644 --- a/ALCameraViewController/Views/CropOverlay.swift +++ b/ALCameraViewController/Views/CropOverlay.swift @@ -18,11 +18,25 @@ internal class CropOverlay: UIView { var topRightCornerLines = [UIView]() var bottomLeftCornerLines = [UIView]() var bottomRightCornerLines = [UIView]() - - let cornerDepth: CGFloat = 3 - let cornerWidth: CGFloat = 20 + + var cornerButtons = [UIButton]() + + let cornerLineDepth: CGFloat = 3 + let cornerLineWidth: CGFloat = 22.5 + var cornerButtonWidth: CGFloat { + return self.cornerLineWidth * 2 + } + let lineWidth: CGFloat = 1 - + + let outterGapRatio: CGFloat = 1/3 + var outterGap: CGFloat { + return self.cornerButtonWidth * self.outterGapRatio + } + + var isResizable: Bool = false + var minimumSize: CGSize = CGSize.zero + internal override init(frame: CGRect) { super.init(frame: frame) createLines() @@ -40,16 +54,16 @@ internal class CropOverlay: UIView { var lineFrame: CGRect switch (i) { case 0: - lineFrame = CGRect(x: 0, y: 0, width: bounds.width, height: lineWidth) + lineFrame = CGRect(x: outterGap, y: outterGap, width: bounds.width - outterGap * 2, height: lineWidth) break case 1: - lineFrame = CGRect(x: bounds.width - lineWidth, y: 0, width: lineWidth, height: bounds.height) + lineFrame = CGRect(x: bounds.width - lineWidth - outterGap, y: outterGap, width: lineWidth, height: bounds.height - outterGap * 2) break case 2: - lineFrame = CGRect(x: 0, y: bounds.height - lineWidth, width: bounds.width, height: lineWidth) + lineFrame = CGRect(x: outterGap, y: bounds.height - lineWidth - outterGap, width: bounds.width - outterGap * 2, height: lineWidth) break case 3: - lineFrame = CGRect(x: 0, y: 0, width: lineWidth, height: bounds.height) + lineFrame = CGRect(x: outterGap, y: outterGap, width: lineWidth, height: bounds.height - outterGap * 2) break default: lineFrame = CGRect.zero @@ -62,51 +76,58 @@ internal class CropOverlay: UIView { let corners = [topLeftCornerLines, topRightCornerLines, bottomLeftCornerLines, bottomRightCornerLines] for i in 0.. UIView { @@ -127,4 +151,59 @@ internal class CropOverlay: UIView { addSubview(line) return line } + + func createButton() -> UIButton { + let button = UIButton() + button.backgroundColor = UIColor.clear + + let dragGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(moveCropOverlay)) + button.addGestureRecognizer(dragGestureRecognizer) + +// // DEBUG +// button.backgroundColor = .yellow +// button.alpha = 0.2 + + addSubview(button) + return button + } + + func moveCropOverlay(gestureRecognizer: UIPanGestureRecognizer) { + if let button = gestureRecognizer.view as? UIButton { + guard isResizable else { + return + } + + 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 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 + layoutSubviews() + + gestureRecognizer.setTranslation(CGPoint.zero, in: self) + } + } else { + if gestureRecognizer.state == .began || gestureRecognizer.state == .changed { + let translation = gestureRecognizer.translation(in: self) + + gestureRecognizer.view!.center = CGPoint(x: gestureRecognizer.view!.center.x + translation.x, y: gestureRecognizer.view!.center.y + translation.y) + gestureRecognizer.setTranslation(CGPoint(x: 0, y: 0), in: self) + } + } + } } diff --git a/Example/ViewController.swift b/Example/ViewController.swift index ef160478..ac344a69 100644 --- a/Example/ViewController.swift +++ b/Example/ViewController.swift @@ -10,17 +10,27 @@ import UIKit class ViewController: UIViewController { - var croppingEnabled: Bool = false var libraryEnabled: Bool = true + var croppingEnabled: Bool = false + var allowResizing: Bool = true + var minimumSize: CGSize = CGSize(width: 60, height: 60) + + var croppingParameters: CroppingParameters { + return CroppingParameters(isEnabled: croppingEnabled, allowResizing: allowResizing, minimumSize: minimumSize) + } @IBOutlet weak var imageView: UIImageView! - + @IBOutlet weak var croppingParametersView: UIView! + @IBOutlet weak var minimumSizeLabel: UILabel! + override func viewDidLoad() { super.viewDidLoad() + + self.imageView.contentMode = .scaleAspectFit } - @IBAction func openCamera(_ sender: AnyObject) { - let cameraViewController = CameraViewController(croppingEnabled: croppingEnabled, allowsLibraryAccess: libraryEnabled) { [weak self] image, asset in + @IBAction func openCamera(_ sender: Any) { + let cameraViewController = CameraViewController(croppingParameters: croppingParameters, allowsLibraryAccess: libraryEnabled) { [weak self] image, asset in self?.imageView.image = image self?.dismiss(animated: true, completion: nil) } @@ -28,8 +38,8 @@ class ViewController: UIViewController { present(cameraViewController, animated: true, completion: nil) } - @IBAction func openLibrary(_ sender: AnyObject) { - let libraryViewController = CameraViewController.imagePickerViewController(croppingEnabled: croppingEnabled) { [weak self] image, asset in + @IBAction func openLibrary(_ sender: Any) { + let libraryViewController = CameraViewController.imagePickerViewController(croppingParameters: croppingParameters) { [weak self] image, asset in self?.imageView.image = image self?.dismiss(animated: true, completion: nil) } @@ -37,12 +47,36 @@ class ViewController: UIViewController { present(libraryViewController, animated: true, completion: nil) } - @IBAction func libraryChanged(_ sender: AnyObject) { + @IBAction func libraryChanged(_ sender: Any) { libraryEnabled = !libraryEnabled } - @IBAction func croppingChanged(_ sender: AnyObject) { - croppingEnabled = !croppingEnabled + @IBAction func croppingChanged(_ sender: Any) { + guard let switchButton = sender as? UISwitch else { + return + } + + croppingEnabled = switchButton.isOn + croppingParametersView.isHidden = !switchButton.isOn } + + @IBAction func resizingChanged(_ sender: Any) { + guard let switchButton = sender as? UISwitch else { + return + } + + allowResizing = switchButton.isOn + } + + @IBAction func minimumSizeChanged(_ sender: Any) { + guard let sliderButton = sender as? UISlider else { + return + } + + let newValue = sliderButton.value + minimumSize = CGSize(width: CGFloat(newValue), height: CGFloat(newValue)) + minimumSizeLabel.text = "Minimum size: \(newValue.rounded())" + } + } diff --git a/Example/ViewController.xib b/Example/ViewController.xib index dde3307b..09bffbff 100644 --- a/Example/ViewController.xib +++ b/Example/ViewController.xib @@ -1,26 +1,35 @@ - - + + + + + - + + + - - + + - + + + + + + + + + + From dca6387a2248fb98865e24f5b503197fb837a7c1 Mon Sep 17 00:00:00 2001 From: Alex Littlejohn Date: Mon, 4 Sep 2017 10:03:03 +0200 Subject: [PATCH 2/4] Allow ragging movement of overlay to be configured --- .../project.pbxproj | 24 +++++++--- .../xcschemes/CameraViewController.xcscheme | 4 +- .../Utilities/CroppingParameters.swift | 10 +++- .../ConfirmViewController.swift | 1 + .../Views/CropOverlay.swift | 13 ++---- Example/ViewController.swift | 32 +++++-------- Example/ViewController.xib | 46 ++++++++++++++++--- 7 files changed, 84 insertions(+), 46 deletions(-) diff --git a/ALCameraViewController.xcodeproj/project.pbxproj b/ALCameraViewController.xcodeproj/project.pbxproj index 1d0b5fa9..35e88351 100644 --- a/ALCameraViewController.xcodeproj/project.pbxproj +++ b/ALCameraViewController.xcodeproj/project.pbxproj @@ -270,7 +270,7 @@ attributes = { LastSwiftMigration = 0700; LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 0800; + LastUpgradeCheck = 0900; ORGANIZATIONNAME = zero; TargetAttributes = { C4829FFA1CAEB16C00541D08 = { @@ -279,7 +279,7 @@ }; FAF0583E1B31618D008E5592 = { CreatedOnToolsVersion = 6.3.2; - DevelopmentTeam = GAPQH4AM76; + DevelopmentTeam = 2466624KEK; LastSwiftMigration = 0800; ProvisioningStyle = Automatic; }; @@ -442,14 +442,20 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -490,14 +496,20 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -529,11 +541,11 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = GAPQH4AM76; + DEVELOPMENT_TEAM = 2466624KEK; INFOPLIST_FILE = "Example/Supporting Files/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.zero.CameraViewController; + PRODUCT_BUNDLE_IDENTIFIER = com.alx2.zero.CameraViewController; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 3.0; }; @@ -545,11 +557,11 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = GAPQH4AM76; + DEVELOPMENT_TEAM = 2466624KEK; INFOPLIST_FILE = "Example/Supporting Files/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.zero.CameraViewController; + PRODUCT_BUNDLE_IDENTIFIER = com.alx2.zero.CameraViewController; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 3.0; }; diff --git a/ALCameraViewController.xcodeproj/xcshareddata/xcschemes/CameraViewController.xcscheme b/ALCameraViewController.xcodeproj/xcshareddata/xcschemes/CameraViewController.xcscheme index 9dec7dbe..f8654c4d 100644 --- a/ALCameraViewController.xcodeproj/xcshareddata/xcschemes/CameraViewController.xcscheme +++ b/ALCameraViewController.xcodeproj/xcshareddata/xcschemes/CameraViewController.xcscheme @@ -1,6 +1,6 @@ @@ -36,6 +37,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/ALCameraViewController/Utilities/CroppingParameters.swift b/ALCameraViewController/Utilities/CroppingParameters.swift index fdc31d9c..88f90acf 100644 --- a/ALCameraViewController/Utilities/CroppingParameters.swift +++ b/ALCameraViewController/Utilities/CroppingParameters.swift @@ -18,16 +18,22 @@ public struct CroppingParameters { /// Default value is set to true. var allowResizing: Bool + /// Allow the cropping area to be moved by the user. + /// Default value is set to false. + var allowMoving: Bool + /// Prevent the user to resize the cropping area below a minimum size. /// Default value is (60, 60). Below this value, corner buttons will overlap. var minimumSize: CGSize - init(isEnabled: Bool = false, - allowResizing: Bool = true, + public init(isEnabled: Bool = false, + allowResizing: Bool = true, + allowMoving: Bool = true, minimumSize: CGSize = CGSize(width: 60, height: 60)) { self.isEnabled = isEnabled self.allowResizing = allowResizing + self.allowMoving = allowMoving self.minimumSize = minimumSize } } diff --git a/ALCameraViewController/ViewController/ConfirmViewController.swift b/ALCameraViewController/ViewController/ConfirmViewController.swift index 0a540225..d9eccd23 100644 --- a/ALCameraViewController/ViewController/ConfirmViewController.swift +++ b/ALCameraViewController/ViewController/ConfirmViewController.swift @@ -70,6 +70,7 @@ public class ConfirmViewController: UIViewController, UIScrollViewDelegate { cropOverlay.isHidden = true cropOverlay.isResizable = croppingParameters.allowResizing + cropOverlay.isMovable = croppingParameters.allowMoving cropOverlay.minimumSize = croppingParameters.minimumSize let spinner = showSpinner() diff --git a/ALCameraViewController/Views/CropOverlay.swift b/ALCameraViewController/Views/CropOverlay.swift index a5192df8..dde94409 100644 --- a/ALCameraViewController/Views/CropOverlay.swift +++ b/ALCameraViewController/Views/CropOverlay.swift @@ -35,6 +35,7 @@ internal class CropOverlay: UIView { } var isResizable: Bool = false + var isMovable: Bool = false var minimumSize: CGSize = CGSize.zero internal override init(frame: CGRect) { @@ -158,21 +159,13 @@ internal class CropOverlay: UIView { let dragGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(moveCropOverlay)) button.addGestureRecognizer(dragGestureRecognizer) - -// // DEBUG -// button.backgroundColor = .yellow -// button.alpha = 0.2 addSubview(button) return button } func moveCropOverlay(gestureRecognizer: UIPanGestureRecognizer) { - if let button = gestureRecognizer.view as? UIButton { - guard isResizable else { - return - } - + if isResizable, let button = gestureRecognizer.view as? UIButton { if gestureRecognizer.state == .began || gestureRecognizer.state == .changed { let translation = gestureRecognizer.translation(in: self) @@ -197,7 +190,7 @@ internal class CropOverlay: UIView { gestureRecognizer.setTranslation(CGPoint.zero, in: self) } - } else { + } else if isMovable { if gestureRecognizer.state == .began || gestureRecognizer.state == .changed { let translation = gestureRecognizer.translation(in: self) diff --git a/Example/ViewController.swift b/Example/ViewController.swift index ac344a69..e4c6aa1b 100644 --- a/Example/ViewController.swift +++ b/Example/ViewController.swift @@ -13,10 +13,11 @@ class ViewController: UIViewController { var libraryEnabled: Bool = true var croppingEnabled: Bool = false var allowResizing: Bool = true + var allowMoving: Bool = false var minimumSize: CGSize = CGSize(width: 60, height: 60) var croppingParameters: CroppingParameters { - return CroppingParameters(isEnabled: croppingEnabled, allowResizing: allowResizing, minimumSize: minimumSize) + return CroppingParameters(isEnabled: croppingEnabled, allowResizing: allowResizing, allowMoving: allowMoving, minimumSize: minimumSize) } @IBOutlet weak var imageView: UIImageView! @@ -51,32 +52,23 @@ class ViewController: UIViewController { libraryEnabled = !libraryEnabled } - @IBAction func croppingChanged(_ sender: Any) { - guard let switchButton = sender as? UISwitch else { - return - } - - croppingEnabled = switchButton.isOn - croppingParametersView.isHidden = !switchButton.isOn + @IBAction func croppingChanged(_ sender: UISwitch) { + croppingEnabled = sender.isOn + croppingParametersView.isHidden = !sender.isOn } - @IBAction func resizingChanged(_ sender: Any) { - guard let switchButton = sender as? UISwitch else { - return - } - - allowResizing = switchButton.isOn + @IBAction func resizingChanged(_ sender: UISwitch) { + allowResizing = sender.isOn } - @IBAction func minimumSizeChanged(_ sender: Any) { - guard let sliderButton = sender as? UISlider else { - return - } + @IBAction func movingChanged(_ sender: UISwitch) { + allowMoving = sender.isOn + } - let newValue = sliderButton.value + @IBAction func minimumSizeChanged(_ sender: UISlider) { + let newValue = sender.value minimumSize = CGSize(width: CGFloat(newValue), height: CGFloat(newValue)) minimumSizeLabel.text = "Minimum size: \(newValue.rounded())" } - } diff --git a/Example/ViewController.xib b/Example/ViewController.xib index 09bffbff..cb3d4f40 100644 --- a/Example/ViewController.xib +++ b/Example/ViewController.xib @@ -1,13 +1,18 @@ - + - + + + + AppleSDGothicNeo-Regular + + @@ -91,17 +96,17 @@ From 29dfcbaddd85cb5db9150823421eb1d1f931b456 Mon Sep 17 00:00:00 2001 From: Alex Littlejohn Date: Mon, 4 Sep 2017 10:05:41 +0200 Subject: [PATCH 3/4] Reset development team --- ALCameraViewController.xcodeproj/project.pbxproj | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ALCameraViewController.xcodeproj/project.pbxproj b/ALCameraViewController.xcodeproj/project.pbxproj index 35e88351..a85be7ed 100644 --- a/ALCameraViewController.xcodeproj/project.pbxproj +++ b/ALCameraViewController.xcodeproj/project.pbxproj @@ -279,7 +279,7 @@ }; FAF0583E1B31618D008E5592 = { CreatedOnToolsVersion = 6.3.2; - DevelopmentTeam = 2466624KEK; + DevelopmentTeam = GAPQH4AM76; LastSwiftMigration = 0800; ProvisioningStyle = Automatic; }; @@ -541,11 +541,11 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = 2466624KEK; + DEVELOPMENT_TEAM = GAPQH4AM76; INFOPLIST_FILE = "Example/Supporting Files/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.alx2.zero.CameraViewController; + PRODUCT_BUNDLE_IDENTIFIER = com.zero.CameraViewController; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 3.0; }; @@ -557,11 +557,11 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = 2466624KEK; + DEVELOPMENT_TEAM = GAPQH4AM76; INFOPLIST_FILE = "Example/Supporting Files/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.alx2.zero.CameraViewController; + PRODUCT_BUNDLE_IDENTIFIER = com.zero.CameraViewController; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 3.0; }; From df98b1972bb3c049f9aabfaad47ef5fcf6c96079 Mon Sep 17 00:00:00 2001 From: Alex Littlejohn Date: Mon, 4 Sep 2017 10:05:50 +0200 Subject: [PATCH 4/4] v2.0.0 --- ALCameraViewController.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ALCameraViewController.podspec b/ALCameraViewController.podspec index 17cd1fe7..ffedc4f6 100644 --- a/ALCameraViewController.podspec +++ b/ALCameraViewController.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = "ALCameraViewController" - spec.version = "1.4.1" + spec.version = "2.0.0" spec.summary = "A camera view controller with custom image picker and image cropping." spec.source = { :git => "https://github.com/AlexLittlejohn/ALCameraViewController.git", :tag => spec.version.to_s } spec.requires_arc = true