diff --git a/Example/MantisExample.xcodeproj/project.pbxproj b/Example/MantisExample.xcodeproj/project.pbxproj index 0f0b85af..9b36201a 100644 --- a/Example/MantisExample.xcodeproj/project.pbxproj +++ b/Example/MantisExample.xcodeproj/project.pbxproj @@ -7,7 +7,8 @@ objects = { /* Begin PBXBuildFile section */ - 1F3B49FC588AD98287DE4267 /* libPods-MantisExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B8C6829EE0F5C72DC10C396B /* libPods-MantisExample.a */; }; + 1AC3B46DF36D97844A4CEA4E /* Pods_MantisExampleTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B100014666B3974EEDE05B0C /* Pods_MantisExampleTests.framework */; }; + 37F095D77401112C083BB830 /* Pods_MantisExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A7EAB9797A43C2A34E26823 /* Pods_MantisExample.framework */; }; 5F0852B5237918510031B75D /* ImagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F0852B4237918510031B75D /* ImagePicker.swift */; }; 5FC10B15217A9EDF00582874 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC10B14217A9EDF00582874 /* AppDelegate.swift */; }; 5FC10B17217A9EDF00582874 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC10B16217A9EDF00582874 /* ViewController.swift */; }; @@ -17,7 +18,6 @@ 5FC10B2A217A9EE100582874 /* MantisTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC10B29217A9EE100582874 /* MantisTests.swift */; }; 5FD045EA245619D400B9D3D2 /* CustomizedCropToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FD045E9245619D400B9D3D2 /* CustomizedCropToolbar.swift */; }; 5FEBA88221961E4A0018DE62 /* EmbeddedCropViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FEBA88121961E4A0018DE62 /* EmbeddedCropViewController.swift */; }; - AF2FE35191AA9D90825785AE /* libPods-MantisExampleTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 652EE158B8C4F04DF075F81D /* libPods-MantisExampleTests.a */; }; CCBF2F602525B3050081B8FE /* CustomizedCropToolbarWithoutList.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCBF2F5F2525B3050081B8FE /* CustomizedCropToolbarWithoutList.swift */; }; /* End PBXBuildFile section */ @@ -32,6 +32,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 3A7EAB9797A43C2A34E26823 /* Pods_MantisExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MantisExample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3FC821D838EDAC646C860226 /* Pods-MantisExampleTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MantisExampleTests.debug.xcconfig"; path = "Target Support Files/Pods-MantisExampleTests/Pods-MantisExampleTests.debug.xcconfig"; sourceTree = ""; }; 5F0852B4237918510031B75D /* ImagePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagePicker.swift; sourceTree = ""; }; 5F21BBB22448036100B7B0C2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -50,10 +51,9 @@ 5FD045E9245619D400B9D3D2 /* CustomizedCropToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomizedCropToolbar.swift; sourceTree = ""; }; 5FEBA88121961E4A0018DE62 /* EmbeddedCropViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmbeddedCropViewController.swift; sourceTree = ""; }; 61C386518C3C25E57F8C83A5 /* Pods-MantisExampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MantisExampleTests.release.xcconfig"; path = "Target Support Files/Pods-MantisExampleTests/Pods-MantisExampleTests.release.xcconfig"; sourceTree = ""; }; - 652EE158B8C4F04DF075F81D /* libPods-MantisExampleTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MantisExampleTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 89C5370690AB91AB238B90E9 /* Pods-MantisExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MantisExample.release.xcconfig"; path = "Target Support Files/Pods-MantisExample/Pods-MantisExample.release.xcconfig"; sourceTree = ""; }; 9326C1CDF121300C5CAAE2BE /* Pods-MantisExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MantisExample.debug.xcconfig"; path = "Target Support Files/Pods-MantisExample/Pods-MantisExample.debug.xcconfig"; sourceTree = ""; }; - B8C6829EE0F5C72DC10C396B /* libPods-MantisExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MantisExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + B100014666B3974EEDE05B0C /* Pods_MantisExampleTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MantisExampleTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CCBF2F5F2525B3050081B8FE /* CustomizedCropToolbarWithoutList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomizedCropToolbarWithoutList.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -62,7 +62,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 1F3B49FC588AD98287DE4267 /* libPods-MantisExample.a in Frameworks */, + 37F095D77401112C083BB830 /* Pods_MantisExample.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -70,7 +70,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - AF2FE35191AA9D90825785AE /* libPods-MantisExampleTests.a in Frameworks */, + 1AC3B46DF36D97844A4CEA4E /* Pods_MantisExampleTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -80,8 +80,8 @@ 5F3BE15921993A7C005C4BD8 /* Frameworks */ = { isa = PBXGroup; children = ( - B8C6829EE0F5C72DC10C396B /* libPods-MantisExample.a */, - 652EE158B8C4F04DF075F81D /* libPods-MantisExampleTests.a */, + 3A7EAB9797A43C2A34E26823 /* Pods_MantisExample.framework */, + B100014666B3974EEDE05B0C /* Pods_MantisExampleTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -147,7 +147,7 @@ 5FC10B0D217A9EDF00582874 /* Sources */, 5FC10B0E217A9EDF00582874 /* Frameworks */, 5FC10B0F217A9EDF00582874 /* Resources */, - 12B88BB2DAEBCE3C6ACE78D7 /* [CP] Copy Pods Resources */, + 5B5D9B6DDD60E517CAAD07B0 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -261,21 +261,21 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 12B88BB2DAEBCE3C6ACE78D7 /* [CP] Copy Pods Resources */ = { + 5B5D9B6DDD60E517CAAD07B0 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-MantisExample/Pods-MantisExample-resources-${CONFIGURATION}-input-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-MantisExample/Pods-MantisExample-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Copy Pods Resources"; + name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-MantisExample/Pods-MantisExample-resources-${CONFIGURATION}-output-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-MantisExample/Pods-MantisExample-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MantisExample/Pods-MantisExample-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MantisExample/Pods-MantisExample-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; B990B0C0191568D739FE220D /* [CP] Check Pods Manifest.lock */ = { diff --git a/Example/Podfile b/Example/Podfile index 0bc8d902..ebd88dae 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -3,7 +3,7 @@ target 'MantisExample' do # Comment the next line if you're not using Swift and don't want to use dynamic frameworks - #use_frameworks! + use_frameworks! # Pods for MantisExample pod 'Mantis', :path => '../' diff --git a/Sources/Mantis/CropView/CropMaskViewManager.swift b/Sources/Mantis/CropView/CropMaskViewManager.swift index b440030b..63311522 100644 --- a/Sources/Mantis/CropView/CropMaskViewManager.swift +++ b/Sources/Mantis/CropView/CropMaskViewManager.swift @@ -16,30 +16,33 @@ class CropMaskViewManager { var cropVisualEffectType: CropVisualEffectType = .blurDark init(with superview: UIView, + cropRatio: CGFloat = 1.0, cropShapeType: CropShapeType = .rect, cropVisualEffectType: CropVisualEffectType = .blurDark) { - setup(in: superview) + setup(in: superview, cropRatio: cropRatio) self.cropShapeType = cropShapeType self.cropVisualEffectType = cropVisualEffectType } - private func setupOverlayView(in view: UIView) { - dimmingView = CropDimmingView(cropShapeType: cropShapeType) + private func setupOverlayView(in view: UIView, cropRatio: CGFloat = 1.0) { + dimmingView = CropDimmingView(cropShapeType: cropShapeType, cropRatio: cropRatio) dimmingView.isUserInteractionEnabled = false dimmingView.alpha = 0 view.addSubview(dimmingView) } - private func setupTranslucencyView(in view: UIView) { - visualEffectView = CropVisualEffectView(cropShapeType: cropShapeType, effectType: cropVisualEffectType) + private func setupTranslucencyView(in view: UIView, cropRatio: CGFloat = 1.0) { + visualEffectView = CropVisualEffectView(cropShapeType: cropShapeType, + effectType: cropVisualEffectType, + cropRatio: cropRatio) visualEffectView.isUserInteractionEnabled = false view.addSubview(visualEffectView) } - func setup(in view: UIView) { - setupOverlayView(in: view) - setupTranslucencyView(in: view) + func setup(in view: UIView, cropRatio: CGFloat = 1.0) { + setupOverlayView(in: view, cropRatio: cropRatio) + setupTranslucencyView(in: view, cropRatio: cropRatio) } func removeMaskViews() { @@ -66,8 +69,8 @@ class CropMaskViewManager { } } - func adaptMaskTo(match cropRect: CGRect) { - dimmingView.adaptMaskTo(match: cropRect) - visualEffectView.adaptMaskTo(match: cropRect) + func adaptMaskTo(match cropRect: CGRect, cropRatio: CGFloat) { + dimmingView.adaptMaskTo(match: cropRect, cropRatio: cropRatio) + visualEffectView.adaptMaskTo(match: cropRect, cropRatio: cropRatio) } } diff --git a/Sources/Mantis/CropView/CropView.swift b/Sources/Mantis/CropView/CropView.swift index b3925ba5..abd1b0db 100644 --- a/Sources/Mantis/CropView/CropView.swift +++ b/Sources/Mantis/CropView/CropView.swift @@ -63,6 +63,7 @@ class CropView: UIView { lazy var scrollView = CropScrollView(frame: bounds) lazy var cropMaskViewManager = CropMaskViewManager(with: self, + cropRatio: CGFloat(getImageRatioH()), cropShapeType: cropShapeType, cropVisualEffectType: cropVisualEffectType) @@ -93,7 +94,13 @@ class CropView: UIView { { [unowned self] _, changed in guard let cropFrame = changed.newValue else { return } self.gridOverlayView.frame = cropFrame - self.cropMaskViewManager.adaptMaskTo(match: cropFrame) + + var cropRatio: CGFloat = 1.0 + if self.gridOverlayView.frame.height != 0 { + cropRatio = self.gridOverlayView.frame.width / self.gridOverlayView.frame.height + } + + self.cropMaskViewManager.adaptMaskTo(match: cropFrame, cropRatio: cropRatio) } initalRender() @@ -191,7 +198,7 @@ class CropView: UIView { func resetUIFrame() { cropMaskViewManager.removeMaskViews() - cropMaskViewManager.setup(in: self) + cropMaskViewManager.setup(in: self, cropRatio: CGFloat(getImageRatioH())) viewModel.resetCropFrame(by: getInitialCropBoxRect()) scrollView.transform = .identity diff --git a/Sources/Mantis/CropViewController/CropViewController.swift b/Sources/Mantis/CropViewController/CropViewController.swift index 90fb6030..5722f9f5 100644 --- a/Sources/Mantis/CropViewController/CropViewController.swift +++ b/Sources/Mantis/CropViewController/CropViewController.swift @@ -81,7 +81,7 @@ public class CropViewController: UIViewController { self.config = config switch config.cropShapeType { - case .circle, .square: + case .circle, .square, .heart: self.config.presetFixedRatioType = .alwaysUsingOnePresetFixedRatio(ratio: 1) default: () diff --git a/Sources/Mantis/MaskBackground/CropDimmingView.swift b/Sources/Mantis/MaskBackground/CropDimmingView.swift index 60671527..b74fdc12 100644 --- a/Sources/Mantis/MaskBackground/CropDimmingView.swift +++ b/Sources/Mantis/MaskBackground/CropDimmingView.swift @@ -9,16 +9,20 @@ import UIKit class CropDimmingView: UIView, CropMaskProtocol { + var innerLayer: CALayer? + var cropShapeType: CropShapeType = .rect + var imageRatio: CGFloat = 1.0 - convenience init(cropShapeType: CropShapeType = .rect) { + convenience init(cropShapeType: CropShapeType = .rect, cropRatio: CGFloat = 1.0) { self.init(frame: CGRect.zero) self.cropShapeType = cropShapeType - initialize() + initialize(cropRatio: cropRatio) } - func setMask() { - let layer = createOverLayer(opacity: 0.5) + func setMask(cropRatio: CGFloat) { + let layer = createOverLayer(opacity: 0.5, cropRatio: cropRatio) self.layer.addSublayer(layer) + innerLayer = layer } } diff --git a/Sources/Mantis/MaskBackground/CropMaskProtocal.swift b/Sources/Mantis/MaskBackground/CropMaskProtocal.swift index 25100af4..1caa406e 100644 --- a/Sources/Mantis/MaskBackground/CropMaskProtocal.swift +++ b/Sources/Mantis/MaskBackground/CropMaskProtocal.swift @@ -13,16 +13,17 @@ fileprivate let initialFrameLength: CGFloat = 1000 protocol CropMaskProtocol where Self: UIView { var cropShapeType: CropShapeType { get set } + var innerLayer: CALayer? { get set } - func initialize() - func setMask() - func adaptMaskTo(match cropRect: CGRect) + func initialize(cropRatio: CGFloat) + func setMask(cropRatio: CGFloat) + func adaptMaskTo(match cropRect: CGRect, cropRatio: CGFloat) } extension CropMaskProtocol { - func initialize() { + func initialize(cropRatio: CGFloat = 1.0) { setInitialFrame() - setMask() + setMask(cropRatio: cropRatio) } private func setInitialFrame() { @@ -37,20 +38,38 @@ extension CropMaskProtocol { self.frame = CGRect(x: x, y: y, width: width, height: height) } - func adaptMaskTo(match cropRect: CGRect) { - let scaleX = cropRect.width / minOverLayerUnit - let scaleY = cropRect.height / minOverLayerUnit + func adaptMaskTo(match cropRect: CGRect, cropRatio: CGFloat) { + let scaleX: CGFloat + switch cropShapeType { + case .roundedRect: + innerLayer?.removeFromSuperlayer() + setMask(cropRatio: cropRatio) + scaleX = cropRect.width / (minOverLayerUnit * cropRatio) + default: + scaleX = cropRect.width / minOverLayerUnit + } + + let scaleY = cropRect.height / minOverLayerUnit + transform = CGAffineTransform(scaleX: scaleX, y: scaleY) - + self.frame.origin.x = cropRect.midX - self.frame.width / 2 self.frame.origin.y = cropRect.midY - self.frame.height / 2 } - func createOverLayer(opacity: Float) -> CAShapeLayer { - let x = bounds.midX - minOverLayerUnit / 2 + func createOverLayer(opacity: Float, cropRatio: CGFloat = 1.0) -> CAShapeLayer { + let coff: CGFloat + switch cropShapeType { + case .roundedRect: + coff = cropRatio + default: + coff = 1 + } + + let x = bounds.midX - minOverLayerUnit * coff / 2 let y = bounds.midY - minOverLayerUnit / 2 - let initialRect = CGRect(x: x, y: y, width: minOverLayerUnit, height: minOverLayerUnit) + let initialRect = CGRect(x: x, y: y, width: minOverLayerUnit * coff, height: minOverLayerUnit) let path = UIBezierPath(rect: self.bounds) diff --git a/Sources/Mantis/MaskBackground/CropVisualEffectView.swift b/Sources/Mantis/MaskBackground/CropVisualEffectView.swift index 68d9f244..8668ed20 100644 --- a/Sources/Mantis/MaskBackground/CropVisualEffectView.swift +++ b/Sources/Mantis/MaskBackground/CropVisualEffectView.swift @@ -9,11 +9,16 @@ import UIKit class CropVisualEffectView: UIVisualEffectView, CropMaskProtocol { + var innerLayer: CALayer? + var cropShapeType: CropShapeType = .rect + var imageRatio: CGFloat = 1.0 fileprivate var translucencyEffect: UIVisualEffect? - convenience init(cropShapeType: CropShapeType = .rect, effectType: CropVisualEffectType = .blurDark) { + convenience init(cropShapeType: CropShapeType = .rect, + effectType: CropVisualEffectType = .blurDark, + cropRatio: CGFloat = 1.0) { let (translucencyEffect, backgroundColor) = CropVisualEffectView.getEffect(byType: effectType) @@ -22,16 +27,18 @@ class CropVisualEffectView: UIVisualEffectView, CropMaskProtocol { self.translucencyEffect = translucencyEffect self.backgroundColor = backgroundColor - initialize() + initialize(cropRatio: cropRatio) } - func setMask() { - let layer = createOverLayer(opacity: 0.98) + func setMask(cropRatio: CGFloat) { + let layer = createOverLayer(opacity: 0.98, cropRatio: cropRatio) let maskView = UIView(frame: self.bounds) maskView.clipsToBounds = true maskView.layer.addSublayer(layer) + innerLayer = layer + self.mask = maskView }