diff --git a/Example/ViewController.swift b/Example/ViewController.swift index ad57a777..c611f4a5 100644 --- a/Example/ViewController.swift +++ b/Example/ViewController.swift @@ -58,7 +58,7 @@ class ViewController: UIViewController, CropViewControllerDelegate { var config = Mantis.Config() config.cropMode = .async - config.cropViewConfig.showRotationDial = false + config.cropViewConfig.showAttachedRotationControlView = false config.showAttachedCropToolbar = false let cropViewController: CustomViewController = Mantis.cropViewController(image: image, config: config) cropViewController.delegate = self @@ -101,7 +101,7 @@ class ViewController: UIViewController, CropViewControllerDelegate { var config = Mantis.Config() config.showAttachedCropToolbar = false - config.cropViewConfig.showRotationDial = false + config.cropViewConfig.showAttachedRotationControlView = false config.cropViewConfig.minimumZoomScale = 2.0 config.cropViewConfig.maximumZoomScale = 10.0 diff --git a/README.md b/README.md index 5de144f3..6c817a31 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ ## Breaking Changes in 2.x.x * Add CropViewConfig * move some properties from Config to CropViewConfig - * make dialConfig as a property of CropViewConfig + * make roationControlViewConfig as a property of CropViewConfig * Refactor CropToolbarConfigProtocol * rename some properties @@ -145,7 +145,7 @@ let cropViewController = Mantis.cropViewController(image: , config: When choose alwaysUsingOnePresetFixedRatio, fixed-ratio setting button does not show. -* If you want to hide rotation dial, set Mantis.Config..cropViewConfig.dialConfig = nil +* If you want to hide rotation control view, set Mantis.Config.cropViewConfig.showAttachedRotationControlView = false * If you want to use ratio list instead of presenter, set Mantis.CropToolbarConfig.ratioCandidatesShowType = .alwaysShowRatioList ```swift diff --git a/Sources/Mantis/CropView/CropView+Touches.swift b/Sources/Mantis/CropView/CropView+Touches.swift index 572b258f..4a4ae853 100644 --- a/Sources/Mantis/CropView/CropView+Touches.swift +++ b/Sources/Mantis/CropView/CropView+Touches.swift @@ -12,8 +12,8 @@ extension CropView { override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { let newPoint = convert(point, to: self) - if let rotationDial = rotationDial, rotationDial.frame.contains(newPoint) { - return rotationDial + if let rotationControlView = rotationControlView, rotationControlView.frame.contains(newPoint) { + return rotationControlView } if !cropViewConfig.disableCropBoxDeformation && isHitGridOverlayView(by: newPoint) { @@ -44,8 +44,7 @@ extension CropView { // A resize event has begun by grabbing the crop UI, so notify delegate delegate?.cropViewDidBeginResize(self) - if touch.view is RotationDial { - viewModel.setTouchRotationBoardStatus() + if touch.view is RotationControlViewProtocol { return } @@ -60,7 +59,7 @@ extension CropView { return } - if touch.view is RotationDial { + if touch.view is RotationControlViewProtocol { return } diff --git a/Sources/Mantis/CropView/CropView.swift b/Sources/Mantis/CropView/CropView.swift index 2e1dedaa..681fc4cc 100644 --- a/Sources/Mantis/CropView/CropView.swift +++ b/Sources/Mantis/CropView/CropView.swift @@ -32,8 +32,6 @@ protocol CropViewDelegate: AnyObject { } class CropView: UIView { - private let angleDashboardHeight: CGFloat = 60 - var image: UIImage let viewModel: CropViewModelProtocol @@ -52,9 +50,11 @@ class CropView: UIView { let cropWorkbenchView: CropWorkbenchViewProtocol let cropMaskViewManager: CropMaskViewManagerProtocol - var rotationDial: RotationDialProtocol? { + var rotationControlView: RotationControlViewProtocol? { didSet { - addSubview(rotationDial!) + if rotationControlView?.isAttachedToCropView == true { + addSubview(rotationControlView!) + } } } @@ -154,14 +154,14 @@ class CropView: UIView { case .degree90Rotating: cropMaskViewManager.showVisualEffectBackground(animated: true) cropAuxiliaryIndicatorView.isHidden = true - rotationDial?.isHidden = true + toggleRotationControlViewIsNeeded(isHidden: true) case .touchImage: cropMaskViewManager.showDimmingBackground(animated: true) cropAuxiliaryIndicatorView.gridLineNumberType = .crop cropAuxiliaryIndicatorView.gridHidden = false case .touchCropboxHandle(let tappedEdge): cropAuxiliaryIndicatorView.handleIndicatorHandleTouched(with: tappedEdge) - rotationDial?.isHidden = true + toggleRotationControlViewIsNeeded(isHidden: true) cropMaskViewManager.showDimmingBackground(animated: true) case .touchRotationBoard: cropAuxiliaryIndicatorView.gridLineNumberType = .rotate @@ -169,12 +169,18 @@ class CropView: UIView { cropMaskViewManager.showDimmingBackground(animated: true) case .betweenOperation: cropAuxiliaryIndicatorView.handleEdgeUntouched() - rotationDial?.isHidden = false - adaptAngleDashboardToCropBox() + toggleRotationControlViewIsNeeded(isHidden: false) + adaptRotationControlViewToCropBoxIfNeeded() cropMaskViewManager.showVisualEffectBackground(animated: true) checkImageStatusChanged() } } + + private func toggleRotationControlViewIsNeeded(isHidden: Bool) { + if rotationControlView?.isAttachedToCropView == true { + rotationControlView?.isHidden = isHidden + } + } private func imageStatusChanged() -> Bool { if viewModel.getTotalRadians() != 0 { return true } @@ -212,7 +218,7 @@ class CropView: UIView { cropWorkbenchView.resetImageContent(by: viewModel.cropBoxFrame) cropAuxiliaryIndicatorView.bringSelfToFront() - setupRotationDial() + setupRotationDialIfNeeded() if aspectRatioLockEnabled { setFixedRatioCropBox() @@ -242,55 +248,63 @@ class CropView: UIView { addSubview(cropAuxiliaryIndicatorView) } - private func setupRotationDial() { - guard cropViewConfig.showRotationDial, let rotationDial = rotationDial else { + private func setupRotationDialIfNeeded() { + guard let rotationControlView = rotationControlView else { return } - rotationDial.reset() - - let boardLength = min(bounds.width, bounds.height) * 0.6 - let dialFrame = CGRect(x: 0, - y: 0, - width: boardLength, - height: angleDashboardHeight) - rotationDial.setup(with: dialFrame) - rotationDial.isUserInteractionEnabled = true - - rotationDial.setRotationCenter(by: cropAuxiliaryIndicatorView.center, of: self) - - rotationDial.didRotate = { [unowned self] angle in + rotationControlView.reset() + rotationControlView.isUserInteractionEnabled = true + + rotationControlView.didUpdateRotationValue = { [unowned self] angle in + self.viewModel.setTouchRotationBoardStatus() self.viewModel.setRotatingStatus(by: angle) } - rotationDial.didFinishedRotate = { [unowned self] in + rotationControlView.didFinishRotation = { [unowned self] in self.viewModel.setBetweenOperationStatus() } + + if rotationControlView.isAttachedToCropView { + let boardLength = min(bounds.width, bounds.height) * 0.6 + let dialFrame = CGRect(x: 0, + y: 0, + width: boardLength, + height: cropViewConfig.rotationControlViewHeight) + + rotationControlView.setupUI(withAllowableFrame: dialFrame) + } - rotationDial.rotateDialPlate(by: Angle(radians: viewModel.radians)) - rotationDial.bringSelfToFront() + if let rotationDial = rotationControlView as? RotationDialProtocol { + rotationDial.setRotationCenter(by: cropAuxiliaryIndicatorView.center, of: self) + } - adaptAngleDashboardToCropBox() + rotationControlView.updateRotationValue(by: Angle(radians: viewModel.radians)) + viewModel.setBetweenOperationStatus() + + adaptRotationControlViewToCropBoxIfNeeded() + rotationControlView.bringSelfToFront() } - private func adaptAngleDashboardToCropBox() { - guard let rotationDial = rotationDial else { return } + private func adaptRotationControlViewToCropBoxIfNeeded() { + guard let rotationControlView = rotationControlView, + rotationControlView.isAttachedToCropView else { return } if Orientation.treatAsPortrait { - rotationDial.transform = CGAffineTransform(rotationAngle: 0) - rotationDial.frame.origin.x = cropAuxiliaryIndicatorView.frame.origin.x + - (cropAuxiliaryIndicatorView.frame.width - rotationDial.frame.width) / 2 - rotationDial.frame.origin.y = cropAuxiliaryIndicatorView.frame.maxY + rotationControlView.transform = CGAffineTransform(rotationAngle: 0) + rotationControlView.frame.origin.x = cropAuxiliaryIndicatorView.frame.origin.x + + (cropAuxiliaryIndicatorView.frame.width - rotationControlView.frame.width) / 2 + rotationControlView.frame.origin.y = cropAuxiliaryIndicatorView.frame.maxY } else if Orientation.isLandscapeLeft { - rotationDial.transform = CGAffineTransform(rotationAngle: -CGFloat.pi / 2) - rotationDial.frame.origin.x = cropAuxiliaryIndicatorView.frame.maxX - rotationDial.frame.origin.y = cropAuxiliaryIndicatorView.frame.origin.y + - (cropAuxiliaryIndicatorView.frame.height - rotationDial.frame.height) / 2 + rotationControlView.transform = CGAffineTransform(rotationAngle: -CGFloat.pi / 2) + rotationControlView.frame.origin.x = cropAuxiliaryIndicatorView.frame.maxX + rotationControlView.frame.origin.y = cropAuxiliaryIndicatorView.frame.origin.y + + (cropAuxiliaryIndicatorView.frame.height - rotationControlView.frame.height) / 2 } else if Orientation.isLandscapeRight { - rotationDial.transform = CGAffineTransform(rotationAngle: CGFloat.pi / 2) - rotationDial.frame.origin.x = cropAuxiliaryIndicatorView.frame.minX - rotationDial.frame.width - rotationDial.frame.origin.y = cropAuxiliaryIndicatorView.frame.origin.y + - (cropAuxiliaryIndicatorView.frame.height - rotationDial.frame.height) / 2 + rotationControlView.transform = CGAffineTransform(rotationAngle: CGFloat.pi / 2) + rotationControlView.frame.origin.x = cropAuxiliaryIndicatorView.frame.minX - rotationControlView.frame.width + rotationControlView.frame.origin.y = cropAuxiliaryIndicatorView.frame.origin.y + + (cropAuxiliaryIndicatorView.frame.height - rotationControlView.frame.height) / 2 } } @@ -411,21 +425,27 @@ extension CropView { let rect = self.bounds var contentRect = CGRect.zero + var rotationControlViewHeight: CGFloat = 0 + + if cropViewConfig.showAttachedRotationControlView && rotationControlView?.isAttachedToCropView == true { + rotationControlViewHeight = cropViewConfig.rotationControlViewHeight + } + if Orientation.treatAsPortrait { contentRect.origin.x = rect.origin.x + cropViewPadding contentRect.origin.y = rect.origin.y + cropViewPadding contentRect.size.width = rect.width - 2 * cropViewPadding - contentRect.size.height = rect.height - 2 * cropViewPadding - angleDashboardHeight + contentRect.size.height = rect.height - 2 * cropViewPadding - rotationControlViewHeight } else if Orientation.isLandscape { - contentRect.size.width = rect.width - 2 * cropViewPadding - angleDashboardHeight + contentRect.size.width = rect.width - 2 * cropViewPadding - rotationControlViewHeight contentRect.size.height = rect.height - 2 * cropViewPadding contentRect.origin.y = rect.origin.y + cropViewPadding if Orientation.isLandscapeLeft { contentRect.origin.x = rect.origin.x + cropViewPadding } else { - contentRect.origin.x = rect.origin.x + cropViewPadding + angleDashboardHeight + contentRect.origin.x = rect.origin.x + cropViewPadding + rotationControlViewHeight } } @@ -690,13 +710,7 @@ extension CropView { func getTotalRadians() -> CGFloat { return viewModel.getTotalRadians() } - - private func setRotation(byRadians radians: CGFloat) { - cropWorkbenchView.transform = CGAffineTransform(rotationAngle: radians) - updatePosition(by: radians) - rotationDial?.rotateDialPlate(to: Angle(radians: radians), animated: false) - } - + func setFixedRatioCropBox(zoom: Bool = true, cropBox: CGRect? = nil) { let refCropBox = cropBox ?? getInitialCropBoxRect() let imageHorizontalToVerticalRatio = ImageHorizontalToVerticalRatio(ratio: getImageHorizontalToVerticalRatio()) @@ -711,7 +725,7 @@ extension CropView { self.viewModel.setBetweenOperationStatus() } - adaptAngleDashboardToCropBox() + adaptRotationControlViewToCropBoxIfNeeded() cropWorkbenchView.updateMinZoomScale() } @@ -884,7 +898,7 @@ extension CropView: CropViewProtocol { } } - func transform(byTransformInfo transformation: Transformation, rotateDial: Bool = true) { + func transform(byTransformInfo transformation: Transformation, isUpdateRotationControlView: Bool = true) { viewModel.setRotatingStatus(by: Angle(radians: transformation.rotation)) if transformation.cropWorkbenchViewBounds != .zero { @@ -900,9 +914,9 @@ extension CropView: CropViewProtocol { viewModel.cropBoxFrame = transformation.maskFrame } - if rotateDial { - rotationDial?.rotateDialPlate(by: Angle(radians: viewModel.radians)) - adaptAngleDashboardToCropBox() + if isUpdateRotationControlView { + rotationControlView?.updateRotationValue(by: Angle(radians: viewModel.radians)) + adaptRotationControlViewToCropBoxIfNeeded() } } @@ -979,7 +993,7 @@ extension CropView: CropViewProtocol { var newTransform = getTransformInfo(byTransformInfo: transformInfo) // The first transform is just for retrieving the final cropBoxFrame - transform(byTransformInfo: newTransform, rotateDial: false) + transform(byTransformInfo: newTransform, isUpdateRotationControlView: false) // The second transform is for adjusting the scale of transformInfo let adjustScale = (viewModel.cropBoxFrame.width / viewModel.cropBoxOriginFrame.width) diff --git a/Sources/Mantis/CropViewConfig.swift b/Sources/Mantis/CropViewConfig.swift index f1a2ce36..65a9109c 100644 --- a/Sources/Mantis/CropViewConfig.swift +++ b/Sources/Mantis/CropViewConfig.swift @@ -42,12 +42,19 @@ public struct CropViewConfig { public var maximumZoomScale: CGFloat = 15 /** - Rotation Dial currently is tightly coupled with other parts of CropView, we see rotation dial as a part of CropView, - so we put dialConfig inside CropViewConfig + Rotation control view currently is tightly coupled with other parts of CropView, we see rotation control view as a part of CropView, + so we put RotationControlViewConfig inside CropViewConfig */ - public var dialConfig = DialConfig() + public var rotationControlViewConfig = RotationControlViewConfig() - public var showRotationDial = true + @available(*, deprecated, message: "Use showAttachedRotationControlView instead") + public var showRotationDial = true { + didSet { + showAttachedRotationControlView = showRotationDial + } + } + + public var showAttachedRotationControlView = true public var padding: CGFloat = 14 { didSet { @@ -59,6 +66,8 @@ public struct CropViewConfig { public var cropActivityIndicatorSize = CGSize(width: 100, height: 100) + public var rotationControlViewHeight: CGFloat = 60 + var minimumCropBoxSize: CGFloat = 42 public var disableCropBoxDeformation = false diff --git a/Sources/Mantis/Mantis.swift b/Sources/Mantis/Mantis.swift index 0b3917c2..fa8ef7b7 100644 --- a/Sources/Mantis/Mantis.swift +++ b/Sources/Mantis/Mantis.swift @@ -27,18 +27,24 @@ import UIKit // MARK: - APIs public func cropViewController(image: UIImage, config: Mantis.Config = Mantis.Config(), - cropToolbar: CropToolbarProtocol = CropToolbar(frame: .zero)) -> Mantis.CropViewController { + cropToolbar: CropToolbarProtocol = CropToolbar(frame: .zero), + rotationControlView: RotationControlViewProtocol? = nil) -> Mantis.CropViewController { let cropViewController = CropViewController(config: config) - cropViewController.cropView = buildCropView(with: image, and: config.cropViewConfig) + cropViewController.cropView = buildCropView(withImage: image, + config: config.cropViewConfig, + rotationControlView: rotationControlView) cropViewController.cropToolbar = cropToolbar return cropViewController } public func cropViewController(image: UIImage, config: Mantis.Config = Mantis.Config(), - cropToolbar: CropToolbarProtocol = CropToolbar(frame: .zero)) -> T { + cropToolbar: CropToolbarProtocol = CropToolbar(frame: .zero), + rotationControlView: RotationControlViewProtocol? = nil) -> T { let cropViewController = T(config: config) - cropViewController.cropView = buildCropView(with: image, and: config.cropViewConfig) + cropViewController.cropView = buildCropView(withImage: image, + config: config.cropViewConfig, + rotationControlView: rotationControlView) cropViewController.cropToolbar = cropToolbar return cropViewController } @@ -46,9 +52,12 @@ public func cropViewController(image: UIImage, public func setupCropViewController(_ cropViewController: Mantis.CropViewController, with image: UIImage, and config: Mantis.Config = Mantis.Config(), - cropToolbar: CropToolbarProtocol = CropToolbar(frame: .zero)) { + cropToolbar: CropToolbarProtocol = CropToolbar(frame: .zero), + rotationControlView: RotationControlViewProtocol? = nil) { cropViewController.config = config - cropViewController.cropView = buildCropView(with: image, and: config.cropViewConfig) + cropViewController.cropView = buildCropView(withImage: image, + config: config.cropViewConfig, + rotationControlView: rotationControlView) cropViewController.cropToolbar = cropToolbar } @@ -84,7 +93,9 @@ private(set) var bundle: Bundle? = { return Mantis.Config.bundle }() -private func buildCropView(with image: UIImage, and cropViewConfig: CropViewConfig) -> CropViewProtocol { +private func buildCropView(withImage image: UIImage, + config cropViewConfig: CropViewConfig, + rotationControlView: RotationControlViewProtocol?) -> CropViewProtocol { let cropAuxiliaryIndicatorView = CropAuxiliaryIndicatorView(frame: .zero, cropBoxHotAreaUnit: cropViewConfig.cropBoxHotAreaUnit, disableCropBoxDeformation: cropViewConfig.disableCropBoxDeformation) @@ -97,7 +108,7 @@ private func buildCropView(with image: UIImage, and cropViewConfig: CropViewConf cropWorkbenchView: buildCropWorkbenchView(with: cropViewConfig, and: imageContainer), cropMaskViewManager: buildCropMaskViewManager(with: cropViewConfig)) - setupRotationDialIfNeeded(with: cropViewConfig, and: cropView) + setupRotationControlViewIfNeeded(withConfig: cropViewConfig, cropView: cropView, rotationControlView: rotationControlView) return cropView } @@ -128,13 +139,23 @@ private func buildCropMaskViewManager(with cropViewConfig: CropViewConfig) -> Cr return CropMaskViewManager(dimmingView: dimmingView, visualEffectView: visualEffectView) } -private func setupRotationDialIfNeeded(with cropViewConfig: CropViewConfig, and cropView: CropView) { - if cropViewConfig.showRotationDial { - let viewModel = RotationDialViewModel() - let dialPlate = RotationDialPlate(frame: .zero, dialConfig: cropViewConfig.dialConfig) - cropView.rotationDial = RotationDial(frame: .zero, - dialConfig: cropViewConfig.dialConfig, - viewModel: viewModel, - dialPlate: dialPlate) +private func setupRotationControlViewIfNeeded(withConfig cropViewConfig: CropViewConfig, + cropView: CropView, + rotationControlView: RotationControlViewProtocol?) { + if let rotationControlView = rotationControlView { + if rotationControlView.isAttachedToCropView == false || + rotationControlView.isAttachedToCropView && cropViewConfig.showAttachedRotationControlView { + cropView.rotationControlView = rotationControlView + } + } else { + if cropViewConfig.showAttachedRotationControlView { + let viewModel = RotationDialViewModel() + let dialPlate = RotationDialPlate(frame: .zero, dialConfig: cropViewConfig.rotationControlViewConfig) + + cropView.rotationControlView = RotationDial(frame: .zero, + dialConfig: cropViewConfig.rotationControlViewConfig, + viewModel: viewModel, + dialPlate: dialPlate) + } } } diff --git a/Sources/Mantis/Protocols/CropViewProtocol.swift b/Sources/Mantis/Protocols/CropViewProtocol.swift index c3de2419..2a135eed 100644 --- a/Sources/Mantis/Protocols/CropViewProtocol.swift +++ b/Sources/Mantis/Protocols/CropViewProtocol.swift @@ -30,7 +30,7 @@ protocol CropViewProtocol: UIView { func handleAlterCropper90Degree() func handlePresetFixedRatio(_ ratio: Double, transformation: Transformation) - func transform(byTransformInfo transformation: Transformation, rotateDial: Bool) + func transform(byTransformInfo transformation: Transformation, isUpdateRotationControlView: Bool) func getTransformInfo(byTransformInfo transformInfo: Transformation) -> Transformation func getTransformInfo(byNormalizedInfo normalizedInfo: CGRect) -> Transformation func processPresetTransformation(completion: (Transformation) -> Void) diff --git a/Sources/Mantis/Protocols/RotationDialProtocol.swift b/Sources/Mantis/Protocols/RotationDialProtocol.swift index 96a4a90e..427b962e 100644 --- a/Sources/Mantis/Protocols/RotationDialProtocol.swift +++ b/Sources/Mantis/Protocols/RotationDialProtocol.swift @@ -7,17 +7,50 @@ import UIKit -protocol RotationDialProtocol: UIView { +public protocol RotationControlViewProtocol: UIView { + /** + Set it to true if you want CropView to control the frame of your own rotation control view. + Otherwise set it to false + */ + var isAttachedToCropView: Bool { get set } + + /** + It should be called every time updating the roation value by your own rotaion control view + */ + var didUpdateRotationValue: (_ angle: Angle) -> Void { get set } + + /** + It should be called every time updating the rotation value by your own roation conrol view is done + */ + var didFinishRotation: () -> Void { get set } + + /** + The allowableFrame is set by CropView. No need to implement it if isAttachedToCropView is false + */ + func setupUI(withAllowableFrame allowableFrame: CGRect) + + /** + - Return true when the value does not exceeds the limitation or there is no limitation + - Return false when the value exceeds the limitation + */ + @discardableResult func updateRotationValue(by angle: Angle) -> Bool + + /** + Reset rotation control view to initial status + */ + func reset() +} + +extension RotationControlViewProtocol { + func setupUI(withAllowableFrame allowableFrame: CGRect) {} +} + +protocol RotationDialProtocol: RotationControlViewProtocol { var pointerHeight: CGFloat { get set } var spanBetweenDialPlateAndPointer: CGFloat { get set } var pointerWidth: CGFloat { get set } - var didRotate: (_ angle: Angle) -> Void { get set } - var didFinishedRotate: () -> Void { get set } - func setup(with frame: CGRect) - @discardableResult func rotateDialPlate(by angle: Angle) -> Bool + func setRotationCenter(by point: CGPoint, of view: UIView) func rotateDialPlate(to angle: Angle, animated: Bool) func getRotationAngle() -> Angle - func setRotationCenter(by point: CGPoint, of view: UIView) - func reset() } diff --git a/Sources/Mantis/RotationDial/CGAngle.swift b/Sources/Mantis/RotationDial/CGAngle.swift index 48bba123..45b9526a 100644 --- a/Sources/Mantis/RotationDial/CGAngle.swift +++ b/Sources/Mantis/RotationDial/CGAngle.swift @@ -1,6 +1,6 @@ // // Angle.swift -// Puffer +// Mantis // // Created by Echo on 5/22/19. // Copyright © 2019 Echo. All rights reserved. @@ -9,7 +9,7 @@ import UIKit /// Use this class to make angle calculation to be simpler -class Angle: NSObject, Comparable { +public class Angle: NSObject, Comparable { public static func < (lhs: Angle, rhs: Angle) -> Bool { return lhs.radians < rhs.radians } diff --git a/Sources/Mantis/RotationDial/DialConfig.swift b/Sources/Mantis/RotationDial/DialConfig.swift index 8fcf638d..e5d49497 100644 --- a/Sources/Mantis/RotationDial/DialConfig.swift +++ b/Sources/Mantis/RotationDial/DialConfig.swift @@ -9,7 +9,7 @@ import UIKit // MARK: - DialConfig -public struct DialConfig { +public struct RotationControlViewConfig { public init() {} public var margin: Double = 10 { diff --git a/Sources/Mantis/RotationDial/RotationDial+Touches.swift b/Sources/Mantis/RotationDial/RotationDial+Touches.swift index 61b23940..29bfe765 100644 --- a/Sources/Mantis/RotationDial/RotationDial+Touches.swift +++ b/Sources/Mantis/RotationDial/RotationDial+Touches.swift @@ -39,7 +39,7 @@ extension RotationDial { public override func touchesEnded(_ touches: Set, with event: UIEvent?) { super.touchesEnded(touches, with: event) - didFinishedRotate() + didFinishRotation() viewModel.touchPoint = nil } diff --git a/Sources/Mantis/RotationDial/RotationDial.swift b/Sources/Mantis/RotationDial/RotationDial.swift index 7ef87051..8a494bad 100644 --- a/Sources/Mantis/RotationDial/RotationDial.swift +++ b/Sources/Mantis/RotationDial/RotationDial.swift @@ -25,16 +25,18 @@ import UIKit @IBDesignable final class RotationDial: UIView { + var isAttachedToCropView = true + var pointerHeight: CGFloat = 8 var spanBetweenDialPlateAndPointer: CGFloat = 6 var pointerWidth: CGFloat = 8 * sqrt(2) - var didRotate: (_ angle: Angle) -> Void = { _ in } - var didFinishedRotate: () -> Void = { } + var didUpdateRotationValue: (_ angle: Angle) -> Void = { _ in } + var didFinishRotation: () -> Void = { } var viewModel: RotationDialViewModelProtocol - private var dialConfig: DialConfig + private var dialConfig: RotationControlViewConfig private var angleLimit = Angle(radians: .pi) private var showRadiansLimit: CGFloat = .pi @@ -42,7 +44,10 @@ final class RotationDial: UIView { private var dialPlateHolder: UIView? private var pointer: CAShapeLayer = CAShapeLayer() - init(frame: CGRect, dialConfig: DialConfig, viewModel: RotationDialViewModelProtocol, dialPlate: RotationDialPlate) { + init(frame: CGRect, + dialConfig: RotationControlViewConfig, + viewModel: RotationDialViewModelProtocol, + dialPlate: RotationDialPlate) { self.dialConfig = dialConfig self.viewModel = viewModel self.dialPlate = dialPlate @@ -106,13 +111,13 @@ extension RotationDial { } } - if rotateDialPlate(by: angle) { + if updateRotationValue(by: angle) { let newAngle = getRotationAngle() - didRotate(newAngle) + didUpdateRotationValue(newAngle) } } - private func getDialPlateHolder(by orientation: DialConfig.Orientation) -> UIView { + private func getDialPlateHolder(by orientation: RotationControlViewConfig.Orientation) -> UIView { let view = UIView(frame: bounds) switch orientation { @@ -125,7 +130,7 @@ extension RotationDial { return view } - private func setDialPlateHolder(by orientation: DialConfig.Orientation) { + private func setDialPlateHolder(by orientation: RotationControlViewConfig.Orientation) { switch orientation { case .normal: () @@ -202,8 +207,8 @@ extension RotationDial { } extension RotationDial: RotationDialProtocol { - func setup(with frame: CGRect) { - self.frame = frame + func setupUI(withAllowableFrame allowableFrame: CGRect) { + self.frame = allowableFrame if case .limit(let degreeAngle) = dialConfig.rotationLimitType { angleLimit = Angle(degrees: degreeAngle) @@ -214,12 +219,12 @@ extension RotationDial: RotationDialProtocol { } @discardableResult - func rotateDialPlate(by angle: Angle) -> Bool { + func updateRotationValue(by angle: Angle) -> Bool { guard let dialPlate = dialPlate else { return false } let radians = angle.radians if case .limit = dialConfig.rotationLimitType { - if (getRotationAngle() * angle).radians >= 0 && abs(getRotationAngle().radians + radians) >= angleLimit.radians { + if (getRotationAngle() * angle).radians >= 0 && abs(getRotationAngle().radians + radians) > angleLimit.radians { if radians > 0 { rotateDialPlate(to: angleLimit) diff --git a/Sources/Mantis/RotationDial/RotationDialPlate.swift b/Sources/Mantis/RotationDial/RotationDialPlate.swift index e37cb5f2..73d73f1c 100644 --- a/Sources/Mantis/RotationDial/RotationDialPlate.swift +++ b/Sources/Mantis/RotationDial/RotationDialPlate.swift @@ -52,9 +52,9 @@ class RotationDialPlate: UIView { return layer }() - private var dialConfig: DialConfig! + private var dialConfig: RotationControlViewConfig! - init(frame: CGRect, dialConfig: DialConfig) { + init(frame: CGRect, dialConfig: RotationControlViewConfig) { super.init(frame: frame) self.dialConfig = dialConfig } diff --git a/Tests/MantisTests/Mock/FakeRotationDialView.swift b/Tests/MantisTests/Mock/FakeRotationDialView.swift index 58fb038f..8b62b49d 100644 --- a/Tests/MantisTests/Mock/FakeRotationDialView.swift +++ b/Tests/MantisTests/Mock/FakeRotationDialView.swift @@ -15,15 +15,15 @@ class FakeRotationDialView: UIView, RotationDialProtocol { var pointerWidth: CGFloat = 0 - var didRotate: (Angle) -> Void = { _ in } + var didUpdateRotationValue: (Angle) -> Void = { _ in } - var didFinishedRotate: () -> Void = { } + var didFinishRotation: () -> Void = { } - func setup(with frame: CGRect) { + func setupUI(withAllowableFrame allowableFrame: CGRect) { } - func rotateDialPlate(by angle: Angle) -> Bool { + func updateRotationValue(by angle: Angle) -> Bool { false } diff --git a/Tests/MantisTests/RotateDialTests.swift b/Tests/MantisTests/RotateDialTests.swift index 9f39add1..cfdce6f0 100644 --- a/Tests/MantisTests/RotateDialTests.swift +++ b/Tests/MantisTests/RotateDialTests.swift @@ -15,7 +15,7 @@ final class RotateDialTests: XCTestCase { var viewModel: RotationDialViewModel! override func setUpWithError() throws { - let dialConfig = Config().cropViewConfig.dialConfig + let dialConfig = Config().cropViewConfig.rotationControlViewConfig setup(with: dialConfig) } @@ -23,25 +23,25 @@ final class RotateDialTests: XCTestCase { // Put teardown code here. This method is called after the invocation of each test method in the class. } - private func setup(with dialConfig: DialConfig) { + private func setup(with dialConfig: RotationControlViewConfig) { viewModel = RotationDialViewModel() dialPlate = RotationDialPlate(frame: .zero, dialConfig: dialConfig) dial = RotationDial(frame: .zero, dialConfig: dialConfig, viewModel: viewModel, dialPlate: dialPlate) - dial.setup(with: .zero) + dial.setupUI(withAllowableFrame: .zero) } func testRotateDialPlate() { // Test valid plus rotation with limitation - var dialConfig = Config().cropViewConfig.dialConfig + var dialConfig = Config().cropViewConfig.rotationControlViewConfig dialConfig.rotationLimitType = .limit(degreeAngle: 45) setup(with: dialConfig) var dialPlateTransform = dialPlate.transform var angle = Angle(degrees: 40) - XCTAssertTrue(dial.rotateDialPlate(by: angle)) + XCTAssertTrue(dial.updateRotationValue(by: angle)) XCTAssertEqual(dialPlate.transform, dialPlateTransform.rotated(by: angle.radians)) // Test invalid plus rotation with limitation @@ -50,7 +50,7 @@ final class RotateDialTests: XCTestCase { dialPlateTransform = dialPlate.transform angle = Angle(degrees: 50) - XCTAssertFalse(dial.rotateDialPlate(by: angle)) + XCTAssertFalse(dial.updateRotationValue(by: angle)) XCTAssertNotEqual(dialPlate.transform, dialPlateTransform.rotated(by: angle.radians)) XCTAssertEqual(dialPlate.transform, dialPlateTransform.rotated(by: Angle(degrees: 45).radians)) @@ -60,7 +60,7 @@ final class RotateDialTests: XCTestCase { dialPlateTransform = dialPlate.transform angle = Angle(degrees: -40) - XCTAssertTrue(dial.rotateDialPlate(by: angle)) + XCTAssertTrue(dial.updateRotationValue(by: angle)) XCTAssertEqual(dialPlate.transform, dialPlateTransform.rotated(by: angle.radians)) // Test invalid minus rotation with limitation @@ -69,18 +69,18 @@ final class RotateDialTests: XCTestCase { dialPlateTransform = dialPlate.transform angle = Angle(degrees: -50) - XCTAssertFalse(dial.rotateDialPlate(by: angle)) + XCTAssertFalse(dial.updateRotationValue(by: angle)) XCTAssertNotEqual(dialPlate.transform, dialPlateTransform.rotated(by: angle.radians)) XCTAssertEqual(dialPlate.transform, dialPlateTransform.rotated(by: Angle(degrees: -45).radians)) // Test no limit - dialConfig = Config().cropViewConfig.dialConfig + dialConfig = Config().cropViewConfig.rotationControlViewConfig dialConfig.rotationLimitType = .noLimit setup(with: dialConfig) dialPlateTransform = dialPlate.transform angle = Angle(degrees: 70) - XCTAssertTrue(dial.rotateDialPlate(by: angle)) + XCTAssertTrue(dial.updateRotationValue(by: angle)) XCTAssertEqual(dialPlate.transform, dialPlateTransform.rotated(by: angle.radians)) } }