From 9a1e67af3e7d078d4629d43997d4248d2c4cf7a1 Mon Sep 17 00:00:00 2001 From: Richard Shane <5288729+rickshane@users.noreply.github.com> Date: Mon, 25 Mar 2024 01:59:20 +1100 Subject: [PATCH] feat: Undo/Redo Feature for Mantis (#379) * Undo/Redo Feature for Mantis * Addressing comments in PR * Add MantisExampleTests target back to project - somehow got deleted. * Adressing Comments in Chante List: - added "enableUndo" flag in Config - undo/redo/reset logic checks the "enableUndo" flag in cropViewController to enable functionality - abstracted the TransformRecord logic into the CropViewController class - removed UIButtons from TransformDelegate - moved TransformDelegate in to new file in the Protocols folder * added logic to validate the Catalyst menus (disable undo menus when "enableUndo" is false in Config file. * Address latest round of comments - More private variables. - Embedded controller uses proxy functions to communicate with CropViewController state. - Defaults function implementations in delegate methods. * Rename some delegate methods: cropViewControllerDidEnableUndo -> cropViewControllerDidUpdateEnableStateForUndo cropViewControllerDidEnableRedo -> cropViewControllerDidUpdateEnableStateForRedo cropViewControllerDidEnableReset -> cropViewControllerDidUpdateEnableStateForReset * TransformStack private now * changed actionString declaration. * removed unwanted space * Removed some old code, that was not needed anymore when the function signature changed. * removed func cropViewControllerDidImageTransformed(_ cropViewController: CropViewController) from CropViewControllerDelegate * Remove spaces * removed public from some data types: TransformType TransformRecord TransformStack * TransformDelegate methods no longer public * moved opening brackets up to end of function declarations * removed public modifier in all variables and function signatures in TransformRecord * removed public modifier from all variables and function signatures in TransformStack * Placeholder localization strings for "Change Crop" and "Reset Changes" * Localized "Change Crop" and "Reset Changes" using Google Translate. * updated the README.md file to describe Undo/Redo support. * Changed Chinese language localization strings * Refactoring work: - rename TransformDelegate methods - make TransformStack init a private method * - Fixed a localization bug - Removed a dead deprecation directive * cleaned up property list for TransformRecord * made dictionary passed to TransformRecord have strong type in value field * changed transformDelegate to an optional * made private some variables in TransformStack * Add new files to Mantis.xcodeproj: - TransformStack.swift - TransformRecord.swift - TransformDelegate.swift * remove dead code * Rename delegate methods in CropViewControllerDelegate: - cropViewControllerDidUpdateEnableStateForUndo(_ enable: Bool) -> cropViewController(_ cropViewController: CropViewController, didUpdateEnableStateForUndo enable: Bool) - cropViewControllerDidUpdateEnableStateForRedo(_ enable: Bool) -> cropViewController(_ cropViewController: CropViewController, didUpdateEnableStateForRedo enable: Bool) - cropViewControllerDidUpdateEnableStateForReset(_ enable: Bool) -> cropViewController(_ cropViewController: CropViewController, didUpdateEnableStateForReset enable: Bool) * Remove blanl line * Simplify an optional value boolean test * Remove three unused variables from TransformStack * Address a SwiftLint warning in TransformRecord * Address a SwiftLint warning in TransformRecord * Fixed Unit Tests in Mantis.xcodeproj related to new API for undo/redo feature. * remove first call to transform() in applyCropState() and just set the viewModel's cropBoxFrame to the transform.maskFrame * moved function "pushTransformRecordOntoStack" out of CropViewController and in to TransformStack class * added bounds check in TransformStack.popTransformStack() * added Unit Test for TransformStack * Updated TransformStack Unit Test. --- Example/AppDelegate.swift | 37 ++ Example/Base.lproj/Main.storyboard | 29 +- Example/EmbeddedCropViewController.swift | 100 ++++- .../MantisExample.xcodeproj/project.pbxproj | 38 +- .../xcschemes/MantisExample.xcscheme | 1 + Example/ViewController.swift | 5 +- Example/zh-Hans.lproj/Main.storyboard | 57 +-- Mantis.xcodeproj/project.pbxproj | 28 ++ README.md | 11 + Sources/Mantis/Config.swift | 2 + Sources/Mantis/CropData.swift | 73 +++- .../Mantis/CropView/CropView+Touches.swift | 5 +- .../CropView+UIScrollViewDelegate.swift | 1 + Sources/Mantis/CropView/CropView.swift | 40 ++ .../CropViewController.swift | 349 +++++++++++++----- Sources/Mantis/Helpers/Orientation.swift | 2 +- Sources/Mantis/Helpers/TransformRecord.swift | 88 +++++ Sources/Mantis/Helpers/TransformStack.swift | 73 ++++ Sources/Mantis/Mantis.swift | 2 + .../Protocols/CropToolbarProtocol.swift | 5 + .../CropViewControllerDelegate.swift | 15 +- .../Mantis/Protocols/CropViewProtocol.swift | 3 +- .../Mantis/Protocols/TransformDelegate.swift | 21 ++ .../ar.lproj/MantisLocalizable.strings | 2 + .../de.lproj/MantisLocalizable.strings | 3 +- .../en.lproj/MantisLocalizable.strings | 3 +- .../es.lproj/MantisLocalizable.strings | 2 + .../fr.lproj/MantisLocalizable.strings | 3 +- .../it.lproj/MantisLocalizable.strings | 3 +- .../ja.lproj/MantisLocalizable.strings | 3 +- .../ko-KR.lproj/MantisLocalizable.strings | 2 + .../ko.lproj/MantisLocalizable.strings | 3 +- .../nl.lproj/MantisLocalizable.strings | 3 +- .../pt-PT.lproj/MantisLocalizable.strings | 3 +- .../ru.lproj/MantisLocalizable.strings | 2 + .../tr.lproj/MantisLocalizable.strings | 3 +- .../zh-Hans.lproj/MantisLocalizable.strings | 2 + .../zh-Hant.lproj/MantisLocalizable.strings | 2 + .../RotationDial/RotationDial.swift | 3 + Tests/MantisTests/Mock/FakeCropView.swift | 25 +- .../Mock/FakeCropViewControllerDelegate.swift | 2 +- Tests/MantisTests/TransformStackTests.swift | 82 ++++ 42 files changed, 957 insertions(+), 179 deletions(-) create mode 100644 Sources/Mantis/Helpers/TransformRecord.swift create mode 100644 Sources/Mantis/Helpers/TransformStack.swift create mode 100644 Sources/Mantis/Protocols/TransformDelegate.swift create mode 100644 Tests/MantisTests/TransformStackTests.swift diff --git a/Example/AppDelegate.swift b/Example/AppDelegate.swift index bce2b362..d326818b 100644 --- a/Example/AppDelegate.swift +++ b/Example/AppDelegate.swift @@ -40,3 +40,40 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } } + +extension AppDelegate { + + override func buildMenu(with builder: UIMenuBuilder) { + super.buildMenu(with: builder) + + // Ensure that the builder is modifying the menu bar system. + guard builder.system == UIMenuSystem.main else { return } + + // remove items from Edit menu + builder.remove(menu: .undoRedo) + + // Undo + let undoCommand = UIKeyCommand(title: "Undo", + action: #selector(EmbeddedCropViewController.undoButtonPressed(_:)), + input: "z", + modifierFlags: [.command]) + + + // Redo + let redoCommand = UIKeyCommand(title: "Redo", + action: #selector(EmbeddedCropViewController.redoButtonPressed(_:)), + input: "z", + modifierFlags: [.shift, .command]) + + // Revert + let revertCommand = UIKeyCommand(title: "Revert to Original", + action: #selector(EmbeddedCropViewController.resetButtonPressed(_:)), + input: "r", + modifierFlags: [.alternate]) + + let undoMenu = UIMenu(title: "", identifier: .undoRedo, options: .displayInline, children: [undoCommand, redoCommand, revertCommand ]) + + builder.insertChild(undoMenu, atStartOfMenu: .edit) + + } +} diff --git a/Example/Base.lproj/Main.storyboard b/Example/Base.lproj/Main.storyboard index 54cd9d8c..1070a72a 100644 --- a/Example/Base.lproj/Main.storyboard +++ b/Example/Base.lproj/Main.storyboard @@ -1,9 +1,9 @@ - + - + @@ -174,7 +174,7 @@ - + @@ -247,6 +247,23 @@ + + + + + + + + + + + + + + + + + @@ -270,7 +287,10 @@ + + + @@ -322,5 +342,8 @@ + + + diff --git a/Example/EmbeddedCropViewController.swift b/Example/EmbeddedCropViewController.swift index 0452e31b..2c709084 100644 --- a/Example/EmbeddedCropViewController.swift +++ b/Example/EmbeddedCropViewController.swift @@ -14,21 +14,47 @@ class EmbeddedCropViewController: UIViewController { var image: UIImage? var cropViewController: CropViewController? + weak var toolbarDelegate: CropToolbarDelegate? + var didGetCroppedImage: ((UIImage) -> Void)? @IBOutlet weak var cancelButton: UIBarButtonItem! @IBOutlet weak var doneButton: UIBarButtonItem! @IBOutlet weak var resolutionLabel: UILabel! + @IBOutlet weak var undoButton: UIBarButtonItem! + @IBOutlet weak var redoButton: UIBarButtonItem! + @IBOutlet weak var resetButton: UIBarButtonItem! + override func viewDidLoad() { super.viewDidLoad() + undoButton.title = "Undo" + redoButton.title = "Redo" cancelButton.title = "Cancel" doneButton.title = "Done" + resetButton.title = "Revert" + resolutionLabel.text = "\(getResolution(image: image) ?? "unknown")" view.backgroundColor = .black navigationController?.toolbar.backgroundColor = .black + + self.undoButton.isEnabled = false + self.redoButton.isEnabled = false + self.resetButton.isEnabled = false + } + + @IBAction func undoButtonPressed(_ sender: Any) { + toolbarDelegate?.didSelectUndo() + } + + @IBAction func redoButtonPressed(_ sender: Any) { + toolbarDelegate?.didSelectRedo() + } + + @IBAction func resetButtonPressed(_ sender: Any) { + cropViewController?.didSelectReset() } @IBAction func cancel(_ sender: Any) { @@ -48,9 +74,14 @@ class EmbeddedCropViewController: UIViewController { var config = Mantis.Config() config.cropToolbarConfig.mode = .embedded + config.enableUndoRedo = true + + config.cropToolbarConfig.toolbarButtonOptions = [.counterclockwiseRotate, .clockwiseRotate, .horizontallyFlip, .verticallyFlip] + Mantis.setupCropViewController(cropViewController, with: image, and: config) self.cropViewController = cropViewController + self.toolbarDelegate = cropViewController } private func getResolution(image: UIImage?) -> String? { @@ -59,9 +90,76 @@ class EmbeddedCropViewController: UIViewController { } return nil } + + override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { + + if action == #selector(EmbeddedCropViewController.undoButtonPressed(_:)) || + action == #selector(EmbeddedCropViewController.redoButtonPressed(_:)) || + action == #selector(EmbeddedCropViewController.resetButtonPressed(_:)) { + + guard let toolbarDelegate = toolbarDelegate else { return false } + + return toolbarDelegate.isUndoSupported() + } + + return true + } + + override func validate(_ command: UICommand) { + + guard let toolbarDelegate = toolbarDelegate else { return } + + if toolbarDelegate.isUndoSupported() { + + if command.action == #selector(EmbeddedCropViewController.undoButtonPressed(_:)) { + + let undoString = NSLocalizedString("Undo", comment: "Undo") + + command.title = self.undoButton.isEnabled ? "\(undoString) \(toolbarDelegate.undoActionName())" : undoString + + if !self.undoButton.isEnabled { + command.attributes = [.disabled] + } + } + + if command.action == #selector(EmbeddedCropViewController.redoButtonPressed(_:)) { + + let redoString = NSLocalizedString("Redo", comment: "Redo") + + command.title = self.redoButton.isEnabled ? "\(redoString) \(toolbarDelegate.redoActionName())" : redoString + + if !self.redoButton.isEnabled { + command.attributes = [.disabled] + } + } + + if command.action == #selector(EmbeddedCropViewController.resetButtonPressed(_:)) { + + command.title = NSLocalizedString("Revert to Original", comment: "Revert to Original") + + if !self.resetButton.isEnabled { + command.attributes = [.disabled] + } + } + } + } + } extension EmbeddedCropViewController: CropViewControllerDelegate { + + func cropViewController(_ cropViewController: CropViewController, didUpdateEnableStateForUndo enable: Bool) { + self.undoButton.isEnabled = enable + } + + func cropViewController(_ cropViewController: CropViewController, didUpdateEnableStateForRedo enable: Bool) { + self.redoButton.isEnabled = enable + } + + func cropViewController(_ cropViewController: CropViewController, didUpdateEnableStateForReset enable: Bool) { + self.resetButton.isEnabled = enable + } + func cropViewControllerDidCrop(_ cropViewController: CropViewController, cropped: UIImage, transformation: Transformation, @@ -82,5 +180,5 @@ extension EmbeddedCropViewController: CropViewControllerDelegate { let size = cropViewController.getExpectedCropImageSize() self.resolutionLabel.text = "\(Int(size.width)) x \(Int(size.height)) pixels" } - } + diff --git a/Example/MantisExample.xcodeproj/project.pbxproj b/Example/MantisExample.xcodeproj/project.pbxproj index 8e4e1ea2..500a3985 100644 --- a/Example/MantisExample.xcodeproj/project.pbxproj +++ b/Example/MantisExample.xcodeproj/project.pbxproj @@ -3,12 +3,11 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ - 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 */; }; + 3E5A418D8A9C14BAF7DD36AE /* libPods-MantisExampleTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D9A44CC9D29500B4D273713B /* libPods-MantisExampleTests.a */; }; 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 */; }; @@ -20,6 +19,7 @@ 5FEBA88221961E4A0018DE62 /* EmbeddedCropViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FEBA88121961E4A0018DE62 /* EmbeddedCropViewController.swift */; }; 66F909BD287B601A007E2FC6 /* MyNavigationCropToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F909BC287B601A007E2FC6 /* MyNavigationCropToolbar.swift */; }; 85DDBACD298E5A54004FBFE7 /* CustomViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85DDBACC298E5A54004FBFE7 /* CustomViewController.swift */; }; + 9584621CBB178C95456D3612 /* libPods-MantisExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B1CAEE435A112962971BBC20 /* libPods-MantisExample.a */; }; CCBF2F602525B3050081B8FE /* CustomizedCropToolbarWithoutList.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCBF2F5F2525B3050081B8FE /* CustomizedCropToolbarWithoutList.swift */; }; F24DCFE529A8FA7F00D8E8C1 /* CustomWaitingIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F24DCFE429A8FA7F00D8E8C1 /* CustomWaitingIndicator.swift */; }; /* End PBXBuildFile section */ @@ -35,7 +35,6 @@ /* 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 = ""; }; @@ -58,8 +57,9 @@ 85DDBACC298E5A54004FBFE7 /* CustomViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomViewController.swift; sourceTree = ""; }; 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 = ""; }; - B100014666B3974EEDE05B0C /* Pods_MantisExampleTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MantisExampleTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B1CAEE435A112962971BBC20 /* libPods-MantisExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MantisExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; CCBF2F5F2525B3050081B8FE /* CustomizedCropToolbarWithoutList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomizedCropToolbarWithoutList.swift; sourceTree = ""; }; + D9A44CC9D29500B4D273713B /* libPods-MantisExampleTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MantisExampleTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; F24DCFE429A8FA7F00D8E8C1 /* CustomWaitingIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomWaitingIndicator.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -68,7 +68,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 37F095D77401112C083BB830 /* Pods_MantisExample.framework in Frameworks */, + 9584621CBB178C95456D3612 /* libPods-MantisExample.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -76,7 +76,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 1AC3B46DF36D97844A4CEA4E /* Pods_MantisExampleTests.framework in Frameworks */, + 3E5A418D8A9C14BAF7DD36AE /* libPods-MantisExampleTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -86,8 +86,8 @@ 5F3BE15921993A7C005C4BD8 /* Frameworks */ = { isa = PBXGroup; children = ( - 3A7EAB9797A43C2A34E26823 /* Pods_MantisExample.framework */, - B100014666B3974EEDE05B0C /* Pods_MantisExampleTests.framework */, + B1CAEE435A112962971BBC20 /* libPods-MantisExample.a */, + D9A44CC9D29500B4D273713B /* libPods-MantisExampleTests.a */, ); name = Frameworks; sourceTree = ""; @@ -156,7 +156,7 @@ 5FC10B0D217A9EDF00582874 /* Sources */, 5FC10B0E217A9EDF00582874 /* Frameworks */, 5FC10B0F217A9EDF00582874 /* Resources */, - 800F1564269B4B40CE61575F /* [CP] Embed Pods Frameworks */, + 0EA6FA8883379671AEAFBD66 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -270,21 +270,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; }; - 800F1564269B4B40CE61575F /* [CP] Embed Pods Frameworks */ = { + 0EA6FA8883379671AEAFBD66 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-MantisExample/Pods-MantisExample-frameworks-${CONFIGURATION}-input-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-MantisExample/Pods-MantisExample-resources-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Embed Pods Frameworks"; + name = "[CP] Copy Pods Resources"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-MantisExample/Pods-MantisExample-frameworks-${CONFIGURATION}-output-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-MantisExample/Pods-MantisExample-resources-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MantisExample/Pods-MantisExample-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MantisExample/Pods-MantisExample-resources.sh\"\n"; showEnvVarsInLog = 0; }; B990B0C0191568D739FE220D /* [CP] Check Pods Manifest.lock */ = { @@ -498,7 +498,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES; - DEVELOPMENT_TEAM = 3V26Q55U3S; + DEVELOPMENT_TEAM = Z9GT6JU7N2; INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -524,7 +524,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES; - DEVELOPMENT_TEAM = 3V26Q55U3S; + DEVELOPMENT_TEAM = Z9GT6JU7N2; INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -548,7 +548,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 3V26Q55U3S; + DEVELOPMENT_TEAM = Z9GT6JU7N2; INFOPLIST_FILE = MantisTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -571,7 +571,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 3V26Q55U3S; + DEVELOPMENT_TEAM = Z9GT6JU7N2; INFOPLIST_FILE = MantisTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/Example/MantisExample.xcodeproj/xcshareddata/xcschemes/MantisExample.xcscheme b/Example/MantisExample.xcodeproj/xcshareddata/xcschemes/MantisExample.xcscheme index 2142da76..a16f46ae 100644 --- a/Example/MantisExample.xcodeproj/xcshareddata/xcschemes/MantisExample.xcscheme +++ b/Example/MantisExample.xcodeproj/xcshareddata/xcschemes/MantisExample.xcscheme @@ -34,6 +34,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "ar" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/Example/ViewController.swift b/Example/ViewController.swift index f06ef8d0..8129b81c 100644 --- a/Example/ViewController.swift +++ b/Example/ViewController.swift @@ -83,6 +83,7 @@ class ViewController: UIViewController { height: 704.9696330065433), horizontallyFlipped: true, verticallyFlipped: false) + config.cropToolbarConfig.toolbarButtonOptions = [.clockwiseRotate, .reset, .ratio, .autoAdjust, .horizontallyFlip] config.cropViewConfig.presetTransformationType = .presetInfo(info: transform) config.cropViewConfig.builtInRotationControlViewType = .slideDial() @@ -307,10 +308,6 @@ extension ViewController: CropViewControllerDelegate { dismiss(animated: true) } - func cropViewControllerDidImageTransformed(_ cropViewController: CropViewController, transformation: Transformation) { - print("image is transformed. transformation is \(transformation)") - } - func cropViewController(_ cropViewController: CropViewController, didBecomeResettable resettable: Bool) { print("Is resettable: \(resettable)") } diff --git a/Example/zh-Hans.lproj/Main.storyboard b/Example/zh-Hans.lproj/Main.storyboard index 37a540cb..13dc7967 100644 --- a/Example/zh-Hans.lproj/Main.storyboard +++ b/Example/zh-Hans.lproj/Main.storyboard @@ -1,9 +1,9 @@ - - + + - + @@ -13,11 +13,11 @@ - + - +