From 8feece05a23c66240bde446ffaea13f2e0dfe4ae Mon Sep 17 00:00:00 2001 From: Peter <94098333+p3t3r1t@users.noreply.github.com> Date: Tue, 16 Nov 2021 15:25:50 +0100 Subject: [PATCH] feat: added support to set the rotation limits (#148) * added support to set the rotation limits * changes after feedback * changes after feedback * more feedback, ready for merge :) --- Mantis.xcodeproj/project.pbxproj | 8 +- Sources/Mantis/CropView/CropView.swift | 16 ++-- .../CropViewController.swift | 4 +- Sources/Mantis/Mantis.swift | 17 +++- Sources/Mantis/RotationDial/DialConfig.swift | 96 +++++++++---------- .../Mantis/RotationDial/RotationDial.swift | 40 ++++---- .../RotationDial/RotationDialPlate.swift | 16 ++-- 7 files changed, 100 insertions(+), 97 deletions(-) diff --git a/Mantis.xcodeproj/project.pbxproj b/Mantis.xcodeproj/project.pbxproj index c1f06905..599baf6d 100644 --- a/Mantis.xcodeproj/project.pbxproj +++ b/Mantis.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 269AF94227437EE400F7FAF6 /* DialConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269AF94127437EE400F7FAF6 /* DialConfig.swift */; }; 5F17E40A253535F300A3EB7D /* Orientation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F17E409253535F300A3EB7D /* Orientation.swift */; }; 5F69CC5E26C0629400568B75 /* definitions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F69CC5D26C0629400568B75 /* definitions.swift */; }; 5F7D22AE245BCA8D0015A0D5 /* CropToolbarProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F7D22AD245BCA8D0015A0D5 /* CropToolbarProtocol.swift */; }; @@ -62,7 +63,6 @@ OBJ_93 /* CropVisualEffectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_40 /* CropVisualEffectView.swift */; }; OBJ_94 /* RatioOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_41 /* RatioOptions.swift */; }; OBJ_95 /* CGAngle.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_43 /* CGAngle.swift */; }; - OBJ_96 /* DialConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_44 /* DialConfig.swift */; }; OBJ_97 /* RotationCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_45 /* RotationCalculator.swift */; }; OBJ_98 /* RotationDial+Touches.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_46 /* RotationDial+Touches.swift */; }; OBJ_99 /* RotationDial.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_47 /* RotationDial.swift */; }; @@ -93,6 +93,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 269AF94127437EE400F7FAF6 /* DialConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DialConfig.swift; sourceTree = ""; }; 5F17E409253535F300A3EB7D /* Orientation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Orientation.swift; sourceTree = ""; }; 5F69CC5D26C0629400568B75 /* definitions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = definitions.swift; sourceTree = ""; }; 5F7D22AD245BCA8D0015A0D5 /* CropToolbarProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CropToolbarProtocol.swift; sourceTree = ""; }; @@ -144,7 +145,6 @@ OBJ_40 /* CropVisualEffectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CropVisualEffectView.swift; sourceTree = ""; }; OBJ_41 /* RatioOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RatioOptions.swift; sourceTree = ""; }; OBJ_43 /* CGAngle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGAngle.swift; sourceTree = ""; }; - OBJ_44 /* DialConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DialConfig.swift; sourceTree = ""; }; OBJ_45 /* RotationCalculator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RotationCalculator.swift; sourceTree = ""; }; OBJ_46 /* RotationDial+Touches.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RotationDial+Touches.swift"; sourceTree = ""; }; OBJ_47 /* RotationDial.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RotationDial.swift; sourceTree = ""; }; @@ -263,7 +263,7 @@ isa = PBXGroup; children = ( OBJ_43 /* CGAngle.swift */, - OBJ_44 /* DialConfig.swift */, + 269AF94127437EE400F7FAF6 /* DialConfig.swift */, OBJ_45 /* RotationCalculator.swift */, OBJ_46 /* RotationDial+Touches.swift */, OBJ_47 /* RotationDial.swift */, @@ -565,9 +565,9 @@ FEDAAD8C25205DE900D95667 /* RatioItemView.swift in Sources */, FEDAAD8625205CC300D95667 /* RatioSelector.swift in Sources */, OBJ_93 /* CropVisualEffectView.swift in Sources */, + 269AF94227437EE400F7FAF6 /* DialConfig.swift in Sources */, OBJ_94 /* RatioOptions.swift in Sources */, OBJ_95 /* CGAngle.swift in Sources */, - OBJ_96 /* DialConfig.swift in Sources */, OBJ_97 /* RotationCalculator.swift in Sources */, OBJ_98 /* RotationDial+Touches.swift in Sources */, OBJ_99 /* RotationDial.swift in Sources */, diff --git a/Sources/Mantis/CropView/CropView.swift b/Sources/Mantis/CropView/CropView.swift index 2d79d3ec..6429d53b 100644 --- a/Sources/Mantis/CropView/CropView.swift +++ b/Sources/Mantis/CropView/CropView.swift @@ -37,6 +37,9 @@ let hotAreaUnit: CGFloat = 32 let cropViewPadding:CGFloat = 14.0 class CropView: UIView { + + public var dialConfig = Mantis.Config().dialConfig + var cropShapeType: CropShapeType = .rect var cropVisualEffectType: CropVisualEffectType = .blurDark var angleDashboardHeight: CGFloat = 60 @@ -76,9 +79,10 @@ class CropView: UIView { print("CropView deinit.") } - init(image: UIImage, viewModel: CropViewModel = CropViewModel()) { + init(image: UIImage, viewModel: CropViewModel = CropViewModel(), dialConfig: DialConfig = Mantis.Config().dialConfig) { self.image = image self.viewModel = viewModel + self.dialConfig = dialConfig imageContainer = ImageContainer() gridOverlayView = CropOverlayView() @@ -247,15 +251,9 @@ class CropView: UIView { if rotationDial != nil { rotationDial?.removeFromSuperview() } - - var config = DialConfig.Config() - config.backgroundColor = .clear - config.angleShowLimitType = .limit(angle: CGAngle(degrees: 40)) - config.rotationLimitType = .limit(angle: CGAngle(degrees: 45)) - config.numberShowSpan = 1 - + let boardLength = min(bounds.width, bounds.height) * 0.6 - let rotationDial = RotationDial(frame: CGRect(x: 0, y: 0, width: boardLength, height: angleDashboardHeight), config: config) + let rotationDial = RotationDial(frame: CGRect(x: 0, y: 0, width: boardLength, height: angleDashboardHeight), dialConfig: dialConfig) self.rotationDial = rotationDial rotationDial.isUserInteractionEnabled = true addSubview(rotationDial) diff --git a/Sources/Mantis/CropViewController/CropViewController.swift b/Sources/Mantis/CropViewController/CropViewController.swift index fb2b3477..f074cab2 100644 --- a/Sources/Mantis/CropViewController/CropViewController.swift +++ b/Sources/Mantis/CropViewController/CropViewController.swift @@ -59,7 +59,7 @@ public class CropViewController: UIViewController { public var config = Mantis.Config() private var orientation: UIInterfaceOrientation = .unknown - private lazy var cropView = CropView(image: image, viewModel: CropViewModel()) + private lazy var cropView = CropView(image: image, viewModel: CropViewModel(), dialConfig: config.dialConfig) private var cropToolbar: CropToolbarProtocol private var ratioPresenter: RatioPresenter? private var ratioSelector: RatioSelector? @@ -79,7 +79,7 @@ public class CropViewController: UIViewController { self.image = image self.config = config - + switch config.cropShapeType { case .circle, .square, .heart: self.config.presetFixedRatioType = .alwaysUsingOnePresetFixedRatio(ratio: 1) diff --git a/Sources/Mantis/Mantis.swift b/Sources/Mantis/Mantis.swift index db85898b..3e0ff748 100644 --- a/Sources/Mantis/Mantis.swift +++ b/Sources/Mantis/Mantis.swift @@ -30,6 +30,7 @@ private(set) var bundle: Bundle? = { internal var localizationConfig = LocalizationConfig() +// MARK: - Functions public func cropViewController(image: UIImage, config: Mantis.Config = Mantis.Config(), cropToolbar: CropToolbarProtocol = CropToolbar(frame: CGRect.zero)) -> CropViewController { @@ -56,6 +57,7 @@ public func getCroppedImage(byCropInfo info: CropInfo, andImage image: UIImage) return image.getCroppedImage(byCropInfo: info) } +// MARK: - Type Aliases public typealias Transformation = ( offset: CGPoint, rotation: CGFloat, @@ -68,6 +70,7 @@ public typealias Transformation = ( public typealias CropInfo = (translation: CGPoint, rotation: CGFloat, scale: CGFloat, cropSize: CGSize, imageViewSize: CGSize) +// MARK: - Enums public enum PresetTransformationType { case none case presetInfo(info: Transformation) @@ -139,6 +142,13 @@ public enum FixRatiosShowType { case vetical } +// MARK: - Localization +public class LocalizationConfig { + public var bundle: Bundle? = Mantis.Config.bundle + public var tableName = "MantisLocalizable" +} + +// MARK: - CropToolbarConfig public struct CropToolbarConfig { public var optionButtonFontSize: CGFloat = 14 public var optionButtonFontSizeForPad: CGFloat = 20 @@ -153,11 +163,7 @@ public struct CropToolbarConfig { var includeFixedRatioSettingButton = true } -public class LocalizationConfig { - public var bundle: Bundle? = Mantis.Config.bundle - public var tableName = "MantisLocalizable" -} - +// MARK: - Config public struct Config { public var presetTransformationType: PresetTransformationType = .none public var cropShapeType: CropShapeType = .rect @@ -165,6 +171,7 @@ public struct Config { public var ratioOptions: RatioOptions = .all public var presetFixedRatioType: PresetFixedRatioType = .canUseMultiplePresetFixedRatio() public var showRotationDial = true + public var dialConfig = DialConfig() public var cropToolbarConfig = CropToolbarConfig() public private(set) var localizationConfig = Mantis.localizationConfig diff --git a/Sources/Mantis/RotationDial/DialConfig.swift b/Sources/Mantis/RotationDial/DialConfig.swift index c17bab51..352d2254 100644 --- a/Sources/Mantis/RotationDial/DialConfig.swift +++ b/Sources/Mantis/RotationDial/DialConfig.swift @@ -2,77 +2,75 @@ // DialConfig.swift // Mantis // -// Created by Echo on 5/24/19. +// Created by Echo on 5/22/19. // Copyright © 2019 Echo. All rights reserved. // -import Foundation import UIKit -enum DialConfig { - struct Config { - public init() {} - - public var margin: Double = 10 - public var interactable = false - public var rotationLimitType: RotationLimitType = .noLimit - public var angleShowLimitType: AnglehowLimitType = .noLimit - public var rotationCenterType: RotationCenterType = .useDefault - public var numberShowSpan = 2 - public var orientation: Orientation = .normal - - public var backgroundColor: UIColor = .black - public var bigScaleColor: UIColor = .lightGray - public var smallScaleColor: UIColor = .lightGray - public var indicatorColor: UIColor = .lightGray - public var numberColor: UIColor = .lightGray - public var centerAxisColor: UIColor = .lightGray - - public var theme: Theme = .dark { - didSet { - switch theme { - case .dark: - backgroundColor = .black - bigScaleColor = .lightGray - smallScaleColor = .lightGray - indicatorColor = .lightGray - numberColor = .lightGray - centerAxisColor = .lightGray - case .light: - backgroundColor = .white - bigScaleColor = .darkGray - smallScaleColor = .darkGray - indicatorColor = .darkGray - numberColor = .darkGray - centerAxisColor = .darkGray - } +// MARK: - DialConfig +public struct DialConfig { + public init() {} + + public var margin: Double = 10 + public var interactable = false + public var rotationLimitType: RotationLimitType = .limit(angle: CGAngle(degrees: 45)) + public var angleShowLimitType: AngleShowLimitType = .limit(angle: CGAngle(degrees: 40)) + public var rotationCenterType: RotationCenterType = .useDefault + public var numberShowSpan = 1 + public var orientation: Orientation = .normal + + public var backgroundColor: UIColor = .clear + public var bigScaleColor: UIColor = .lightGray + public var smallScaleColor: UIColor = .lightGray + public var indicatorColor: UIColor = .lightGray + public var numberColor: UIColor = .lightGray + public var centerAxisColor: UIColor = .lightGray + + public var theme: Theme = .dark { + didSet { + switch theme { + case .dark: + backgroundColor = .clear + bigScaleColor = .lightGray + smallScaleColor = .lightGray + indicatorColor = .lightGray + numberColor = .lightGray + centerAxisColor = .lightGray + case .light: + backgroundColor = .clear + bigScaleColor = .darkGray + smallScaleColor = .darkGray + indicatorColor = .darkGray + numberColor = .darkGray + centerAxisColor = .darkGray } } } - - enum RotationCenterType { + + public enum RotationCenterType { case useDefault case custom(CGPoint) } - - enum RotationLimitType { + + public enum AngleShowLimitType { case noLimit case limit(angle: CGAngle) } - - enum AnglehowLimitType { + + public enum RotationLimitType { case noLimit case limit(angle: CGAngle) } - - enum Orientation { + + public enum Orientation { case normal case right case left case upsideDown } - - enum Theme { + + public enum Theme { case dark case light } diff --git a/Sources/Mantis/RotationDial/RotationDial.swift b/Sources/Mantis/RotationDial/RotationDial.swift index ab07c2d3..b74e2da4 100644 --- a/Sources/Mantis/RotationDial/RotationDial.swift +++ b/Sources/Mantis/RotationDial/RotationDial.swift @@ -33,7 +33,7 @@ class RotationDial: UIView { var didRotate: (_ angle: CGAngle) -> Void = { _ in } var didFinishedRotate: () -> Void = { } - var config = DialConfig.Config() + var dialConfig = Mantis.Config().dialConfig private var angleLimit = CGAngle(radians: .pi) private var showRadiansLimit: CGFloat = .pi @@ -53,9 +53,9 @@ class RotationDial: UIView { setup() } - public init(frame: CGRect, config: DialConfig.Config) { + public init(frame: CGRect, dialConfig: DialConfig) { super.init(frame: frame) - setup(with: config) + setup(with: dialConfig) } required init?(coder aDecoder: NSCoder) { @@ -67,14 +67,14 @@ class RotationDial: UIView { extension RotationDial { private func setupUI() { clipsToBounds = true - backgroundColor = config.backgroundColor + backgroundColor = dialConfig.backgroundColor dialPlateHolder?.removeFromSuperview() - dialPlateHolder = getDialPlateHolder(by: config.orientation) + dialPlateHolder = getDialPlateHolder(by: dialConfig.orientation) addSubview(dialPlateHolder!) createDialPlate(in: dialPlateHolder!) setupPointer(in: dialPlateHolder!) - setDialPlateHolder(by: config.orientation) + setDialPlateHolder(by: dialConfig.orientation) } private func setupViewModel() { @@ -90,7 +90,7 @@ extension RotationDial { } private func handleRotation(by angle: CGAngle) { - if case .limit = config.rotationLimitType { + if case .limit = dialConfig.rotationLimitType { guard angle <= angleLimit else { return } @@ -131,8 +131,8 @@ extension RotationDial { } private func createDialPlate(in container: UIView) { - var margin: CGFloat = CGFloat(config.margin) - if case .limit(let angle) = config.angleShowLimitType { + var margin: CGFloat = CGFloat(dialConfig.margin) + if case .limit(let angle) = dialConfig.angleShowLimitType { margin = 0 showRadiansLimit = angle.radians } else { @@ -151,7 +151,7 @@ extension RotationDial { let dialPlateFrame = CGRect(x: (container.frame.width - dialPlateLength) / 2, y: margin - (dialPlateLength - dialPlateShowHeight), width: dialPlateLength, height: dialPlateLength) dialPlate?.removeFromSuperview() - dialPlate = RotationDialPlate(frame: dialPlateFrame, config: config) + dialPlate = RotationDialPlate(frame: dialPlateFrame, dialConfig: dialConfig) container.addSubview(dialPlate!) } @@ -169,7 +169,7 @@ extension RotationDial { path.addLine(to: pointLeft) path.addLine(to: pointRight) path.addLine(to: pointTop) - pointer.fillColor = config.indicatorColor.cgColor + pointer.fillColor = dialConfig.indicatorColor.cgColor pointer.path = path container.layer.addSublayer(pointer) } @@ -177,7 +177,7 @@ extension RotationDial { private func getRotationCenter() -> CGPoint { guard let dialPlate = dialPlate else { return .zero } - if case .custom(let center) = config.rotationCenterType { + if case .custom(let center) = dialConfig.rotationCenterType { return center } else { let point = CGPoint(x: dialPlate.bounds.midX , y: dialPlate.bounds.midY) @@ -190,11 +190,11 @@ extension RotationDial { extension RotationDial { /// Setup the dial with your own config /// - /// - Parameter config: dail config. If not provided, default config will be used - public func setup(with config: DialConfig.Config = DialConfig.Config()) { - self.config = config - - if case .limit(let angle) = config.rotationLimitType { + /// - Parameter dialConfig: dail config. If not provided, default config will be used + public func setup(with dialConfig: DialConfig = Mantis.Config().dialConfig) { + self.dialConfig = dialConfig + + if case .limit(let angle) = dialConfig.rotationLimitType { angleLimit = angle } @@ -207,7 +207,7 @@ extension RotationDial { guard let dialPlate = dialPlate else { return false } let radians = angle.radians - if case .limit = config.rotationLimitType { + if case .limit = dialConfig.rotationLimitType { if (getRotationAngle() * angle).radians > 0 && abs(getRotationAngle().radians + radians) >= angleLimit.radians { if radians > 0 { @@ -227,7 +227,7 @@ extension RotationDial { public func rotateDialPlate(to angle: CGAngle, animated: Bool = false) { let radians = angle.radians - if case .limit = config.rotationLimitType { + if case .limit = dialConfig.rotationLimitType { guard abs(radians) <= angleLimit.radians else { return } @@ -259,6 +259,6 @@ extension RotationDial { public func setRotationCenter(by point: CGPoint, of view: UIView) { let newPoint = view.convert(point, to: self) - config.rotationCenterType = .custom(newPoint) + dialConfig.rotationCenterType = .custom(newPoint) } } diff --git a/Sources/Mantis/RotationDial/RotationDialPlate.swift b/Sources/Mantis/RotationDial/RotationDialPlate.swift index 849103fd..9913ce8e 100644 --- a/Sources/Mantis/RotationDial/RotationDialPlate.swift +++ b/Sources/Mantis/RotationDial/RotationDialPlate.swift @@ -53,11 +53,11 @@ class RotationDialPlate: UIView { return layer }() - var config = DialConfig.Config() + var dialConfig = Mantis.Config().dialConfig - init(frame: CGRect, config: DialConfig.Config = DialConfig.Config()) { + init(frame: CGRect, dialConfig: DialConfig = Mantis.Config().dialConfig) { super.init(frame: frame) - self.config = config + self.dialConfig = dialConfig setup() } @@ -69,7 +69,7 @@ class RotationDialPlate: UIView { let mark = CAShapeLayer() mark.frame = CGRect(x: 0, y: 0, width: 2, height: 2) mark.path = UIBezierPath(ovalIn: mark.bounds).cgPath - mark.fillColor = config.smallScaleColor.cgColor + mark.fillColor = dialConfig.smallScaleColor.cgColor return mark } @@ -78,7 +78,7 @@ class RotationDialPlate: UIView { let mark = CAShapeLayer() mark.frame = CGRect(x: 0, y: 0, width: 4, height: 4) mark.path = UIBezierPath(ovalIn: mark.bounds).cgPath - mark.fillColor = config.bigScaleColor.cgColor + mark.fillColor = dialConfig.bigScaleColor.cgColor return mark } @@ -98,7 +98,7 @@ class RotationDialPlate: UIView { let step = (2 * CGFloat.pi) / CGFloat(bigDegreeScaleNumber) for index in (0 ..< bigDegreeScaleNumber) { - guard index % config.numberShowSpan == 0 else { + guard index % dialConfig.numberShowSpan == 0 else { continue } @@ -110,7 +110,7 @@ class RotationDialPlate: UIView { numberLayer.font = cgFont let angle = (index > bigDegreeScaleNumber / 2 ? index - bigDegreeScaleNumber : index) * 10 numberLayer.string = "\(angle)" - numberLayer.foregroundColor = config.numberColor.cgColor + numberLayer.foregroundColor = dialConfig.numberColor.cgColor let stepChange = CGFloat(index) * step numberLayer.position = CGVector(from:origin, to:startPos).rotate(-stepChange).add(origin.vector).point.checked @@ -146,7 +146,7 @@ class RotationDialPlate: UIView { let radius: CGFloat = 4 layer.frame = CGRect(x: (self.layer.bounds.width - radius) / 2 , y: (self.layer.bounds.height - radius) / 2, width: radius, height: radius) layer.path = UIBezierPath(ovalIn: layer.bounds).cgPath - layer.fillColor = config.centerAxisColor.cgColor + layer.fillColor = dialConfig.centerAxisColor.cgColor self.layer.addSublayer(layer) }