├── .gitignore ├── .travis.yml ├── Example ├── ImageCropper.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── ImageCropper-Example.xcscheme ├── ImageCropper.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── ImageCropper │ ├── AppDelegate.swift │ ├── Base.lproj │ │ └── LaunchScreen.xib │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Info.plist │ ├── Main │ │ ├── Main.storyboard │ │ ├── MainConfigurator.swift │ │ ├── MainPresenter.swift │ │ ├── MainRouter.swift │ │ └── MainViewController.swift │ ├── PostProduction │ │ ├── PostProduction.storyboard │ │ ├── PostProductionConfigurator.swift │ │ ├── PostProductionPresenter.swift │ │ ├── PostProductionRouter.swift │ │ └── PostProductionViewController.swift │ ├── iPad_Landscape.png │ ├── iPad_Portrait.png │ ├── iPhone_Landscape.png │ └── iPhone_Portrait.png ├── Podfile ├── Podfile.lock ├── Pods │ ├── Local Podspecs │ │ └── ImageCropper.podspec.json │ ├── Manifest.lock │ ├── Pods.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── Target Support Files │ │ ├── ImageCropper │ │ ├── ImageCropper-dummy.m │ │ ├── ImageCropper-prefix.pch │ │ ├── ImageCropper-umbrella.h │ │ ├── ImageCropper.modulemap │ │ ├── ImageCropper.xcconfig │ │ └── Info.plist │ │ ├── Pods-ImageCropper_Example │ │ ├── Info.plist │ │ ├── Pods-ImageCropper_Example-acknowledgements.markdown │ │ ├── Pods-ImageCropper_Example-acknowledgements.plist │ │ ├── Pods-ImageCropper_Example-dummy.m │ │ ├── Pods-ImageCropper_Example-frameworks.sh │ │ ├── Pods-ImageCropper_Example-resources.sh │ │ ├── Pods-ImageCropper_Example-umbrella.h │ │ ├── Pods-ImageCropper_Example.debug.xcconfig │ │ ├── Pods-ImageCropper_Example.modulemap │ │ └── Pods-ImageCropper_Example.release.xcconfig │ │ └── Pods-ImageCropper_Tests │ │ ├── Info.plist │ │ ├── Pods-ImageCropper_Tests-acknowledgements.markdown │ │ ├── Pods-ImageCropper_Tests-acknowledgements.plist │ │ ├── Pods-ImageCropper_Tests-dummy.m │ │ ├── Pods-ImageCropper_Tests-frameworks.sh │ │ ├── Pods-ImageCropper_Tests-resources.sh │ │ ├── Pods-ImageCropper_Tests-umbrella.h │ │ ├── Pods-ImageCropper_Tests.debug.xcconfig │ │ ├── Pods-ImageCropper_Tests.modulemap │ │ └── Pods-ImageCropper_Tests.release.xcconfig └── Tests │ ├── Info.plist │ └── Tests.swift ├── ImageCropper.png ├── ImageCropper.podspec ├── ImageCropper ├── Assets │ └── .gitkeep └── Classes │ ├── .gitkeep │ ├── ImageCropper.xib │ ├── ImageCropperConfiguration.swift │ ├── ImageCropperConfigurator.swift │ ├── ImageCropperModel.swift │ ├── ImageCropperPresenter.swift │ ├── ImageCropperRouter.swift │ ├── ImageCropperViewController.swift │ └── UIImage+NormalizeOrientation.swift ├── LICENSE ├── README.md └── _Pods.xcodeproj /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 26 | # Carthage/Checkouts 27 | 28 | Carthage/Build 29 | 30 | # We recommend against adding the Pods directory to your .gitignore. However 31 | # you should judge for yourself, the pros and cons are mentioned at: 32 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 33 | # 34 | # Note: if you ignore the Pods directory, make sure to uncomment 35 | # `pod install` in .travis.yml 36 | # 37 | # Pods/ 38 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * https://www.objc.io/issues/6-build-tools/travis-ci/ 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode7.3 6 | language: objective-c 7 | # cache: cocoapods 8 | # podfile: Example/Podfile 9 | # before_install: 10 | # - gem install cocoapods # Since Travis is not always on latest version 11 | # - pod install --project-directory=Example 12 | script: 13 | - set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/ImageCropper.xcworkspace -scheme ImageCropper-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty 14 | - pod lib lint 15 | -------------------------------------------------------------------------------- /Example/ImageCropper.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1B6CBB4420D8D67D003E4136 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1B6CBB3920D8D67D003E4136 /* Main.storyboard */; }; 11 | 1B6CBB4520D8D67D003E4136 /* MainConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B6CBB3A20D8D67D003E4136 /* MainConfigurator.swift */; }; 12 | 1B6CBB4620D8D67D003E4136 /* MainPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B6CBB3B20D8D67D003E4136 /* MainPresenter.swift */; }; 13 | 1B6CBB4720D8D67D003E4136 /* MainRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B6CBB3C20D8D67D003E4136 /* MainRouter.swift */; }; 14 | 1B6CBB4820D8D67D003E4136 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B6CBB3D20D8D67D003E4136 /* MainViewController.swift */; }; 15 | 1B6CBB4920D8D67D003E4136 /* PostProduction.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1B6CBB3F20D8D67D003E4136 /* PostProduction.storyboard */; }; 16 | 1B6CBB4A20D8D67D003E4136 /* PostProductionConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B6CBB4020D8D67D003E4136 /* PostProductionConfigurator.swift */; }; 17 | 1B6CBB4B20D8D67D003E4136 /* PostProductionPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B6CBB4120D8D67D003E4136 /* PostProductionPresenter.swift */; }; 18 | 1B6CBB4C20D8D67D003E4136 /* PostProductionRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B6CBB4220D8D67D003E4136 /* PostProductionRouter.swift */; }; 19 | 1B6CBB4D20D8D67D003E4136 /* PostProductionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B6CBB4320D8D67D003E4136 /* PostProductionViewController.swift */; }; 20 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; 21 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; 22 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; }; 23 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; }; 24 | D23D4F6417D530DA5EB52BA4 /* Pods_ImageCropper_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76DFFA1EB8A1D517ADBA238E /* Pods_ImageCropper_Tests.framework */; }; 25 | F38A5C1A573E594C639B5C3E /* Pods_ImageCropper_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28CBDA345B9A672EECFE4D8D /* Pods_ImageCropper_Example.framework */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXContainerItemProxy section */ 29 | 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = 607FACC81AFB9204008FA782 /* Project object */; 32 | proxyType = 1; 33 | remoteGlobalIDString = 607FACCF1AFB9204008FA782; 34 | remoteInfo = ImageCropper; 35 | }; 36 | /* End PBXContainerItemProxy section */ 37 | 38 | /* Begin PBXFileReference section */ 39 | 13D19B337353CD78EA001DE9 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 40 | 1B6CBB3920D8D67D003E4136 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; 41 | 1B6CBB3A20D8D67D003E4136 /* MainConfigurator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainConfigurator.swift; sourceTree = ""; }; 42 | 1B6CBB3B20D8D67D003E4136 /* MainPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainPresenter.swift; sourceTree = ""; }; 43 | 1B6CBB3C20D8D67D003E4136 /* MainRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainRouter.swift; sourceTree = ""; }; 44 | 1B6CBB3D20D8D67D003E4136 /* MainViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; 45 | 1B6CBB3F20D8D67D003E4136 /* PostProduction.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = PostProduction.storyboard; sourceTree = ""; }; 46 | 1B6CBB4020D8D67D003E4136 /* PostProductionConfigurator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostProductionConfigurator.swift; sourceTree = ""; }; 47 | 1B6CBB4120D8D67D003E4136 /* PostProductionPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostProductionPresenter.swift; sourceTree = ""; }; 48 | 1B6CBB4220D8D67D003E4136 /* PostProductionRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostProductionRouter.swift; sourceTree = ""; }; 49 | 1B6CBB4320D8D67D003E4136 /* PostProductionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostProductionViewController.swift; sourceTree = ""; }; 50 | 28CBDA345B9A672EECFE4D8D /* Pods_ImageCropper_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ImageCropper_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | 5D1C6A0B6977B1B812FE53AA /* ImageCropper.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = ImageCropper.podspec; path = ../ImageCropper.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 52 | 607FACD01AFB9204008FA782 /* ImageCropper_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ImageCropper_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 54 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 55 | 607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 56 | 607FACDF1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 57 | 607FACE51AFB9204008FA782 /* ImageCropper_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ImageCropper_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 58 | 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 59 | 607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; }; 60 | 66A88AA3BF01F50AA421B4DF /* Pods-ImageCropper_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageCropper_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ImageCropper_Example/Pods-ImageCropper_Example.debug.xcconfig"; sourceTree = ""; }; 61 | 76DFFA1EB8A1D517ADBA238E /* Pods_ImageCropper_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ImageCropper_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 62 | 8AE21990F1E34AF0C59BE53C /* Pods-ImageCropper_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageCropper_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ImageCropper_Tests/Pods-ImageCropper_Tests.debug.xcconfig"; sourceTree = ""; }; 63 | 9633BAF6CBDD8C7FCCF3D640 /* Pods-ImageCropper_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageCropper_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-ImageCropper_Example/Pods-ImageCropper_Example.release.xcconfig"; sourceTree = ""; }; 64 | B8EA65C942D40611F7D0021C /* Pods-ImageCropper_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageCropper_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-ImageCropper_Tests/Pods-ImageCropper_Tests.release.xcconfig"; sourceTree = ""; }; 65 | EF219414D7D69B9406560937 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 66 | /* End PBXFileReference section */ 67 | 68 | /* Begin PBXFrameworksBuildPhase section */ 69 | 607FACCD1AFB9204008FA782 /* Frameworks */ = { 70 | isa = PBXFrameworksBuildPhase; 71 | buildActionMask = 2147483647; 72 | files = ( 73 | F38A5C1A573E594C639B5C3E /* Pods_ImageCropper_Example.framework in Frameworks */, 74 | ); 75 | runOnlyForDeploymentPostprocessing = 0; 76 | }; 77 | 607FACE21AFB9204008FA782 /* Frameworks */ = { 78 | isa = PBXFrameworksBuildPhase; 79 | buildActionMask = 2147483647; 80 | files = ( 81 | D23D4F6417D530DA5EB52BA4 /* Pods_ImageCropper_Tests.framework in Frameworks */, 82 | ); 83 | runOnlyForDeploymentPostprocessing = 0; 84 | }; 85 | /* End PBXFrameworksBuildPhase section */ 86 | 87 | /* Begin PBXGroup section */ 88 | 1B6CBB3820D8D67D003E4136 /* Main */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | 1B6CBB3920D8D67D003E4136 /* Main.storyboard */, 92 | 1B6CBB3A20D8D67D003E4136 /* MainConfigurator.swift */, 93 | 1B6CBB3B20D8D67D003E4136 /* MainPresenter.swift */, 94 | 1B6CBB3C20D8D67D003E4136 /* MainRouter.swift */, 95 | 1B6CBB3D20D8D67D003E4136 /* MainViewController.swift */, 96 | ); 97 | path = Main; 98 | sourceTree = ""; 99 | }; 100 | 1B6CBB3E20D8D67D003E4136 /* PostProduction */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 1B6CBB3F20D8D67D003E4136 /* PostProduction.storyboard */, 104 | 1B6CBB4020D8D67D003E4136 /* PostProductionConfigurator.swift */, 105 | 1B6CBB4120D8D67D003E4136 /* PostProductionPresenter.swift */, 106 | 1B6CBB4220D8D67D003E4136 /* PostProductionRouter.swift */, 107 | 1B6CBB4320D8D67D003E4136 /* PostProductionViewController.swift */, 108 | ); 109 | path = PostProduction; 110 | sourceTree = ""; 111 | }; 112 | 607FACC71AFB9204008FA782 = { 113 | isa = PBXGroup; 114 | children = ( 115 | 607FACF51AFB993E008FA782 /* Podspec Metadata */, 116 | 607FACD21AFB9204008FA782 /* Example for ImageCropper */, 117 | 607FACE81AFB9204008FA782 /* Tests */, 118 | 607FACD11AFB9204008FA782 /* Products */, 119 | 71592CF59DD4B8689B5F13B4 /* Pods */, 120 | 9F1F943CBE7ADC137F2CA991 /* Frameworks */, 121 | ); 122 | sourceTree = ""; 123 | }; 124 | 607FACD11AFB9204008FA782 /* Products */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 607FACD01AFB9204008FA782 /* ImageCropper_Example.app */, 128 | 607FACE51AFB9204008FA782 /* ImageCropper_Tests.xctest */, 129 | ); 130 | name = Products; 131 | sourceTree = ""; 132 | }; 133 | 607FACD21AFB9204008FA782 /* Example for ImageCropper */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */, 137 | 1B6CBB3820D8D67D003E4136 /* Main */, 138 | 1B6CBB3E20D8D67D003E4136 /* PostProduction */, 139 | 607FACDC1AFB9204008FA782 /* Images.xcassets */, 140 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */, 141 | 607FACD31AFB9204008FA782 /* Supporting Files */, 142 | ); 143 | name = "Example for ImageCropper"; 144 | path = ImageCropper; 145 | sourceTree = ""; 146 | }; 147 | 607FACD31AFB9204008FA782 /* Supporting Files */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | 607FACD41AFB9204008FA782 /* Info.plist */, 151 | ); 152 | name = "Supporting Files"; 153 | sourceTree = ""; 154 | }; 155 | 607FACE81AFB9204008FA782 /* Tests */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | 607FACEB1AFB9204008FA782 /* Tests.swift */, 159 | 607FACE91AFB9204008FA782 /* Supporting Files */, 160 | ); 161 | path = Tests; 162 | sourceTree = ""; 163 | }; 164 | 607FACE91AFB9204008FA782 /* Supporting Files */ = { 165 | isa = PBXGroup; 166 | children = ( 167 | 607FACEA1AFB9204008FA782 /* Info.plist */, 168 | ); 169 | name = "Supporting Files"; 170 | sourceTree = ""; 171 | }; 172 | 607FACF51AFB993E008FA782 /* Podspec Metadata */ = { 173 | isa = PBXGroup; 174 | children = ( 175 | 5D1C6A0B6977B1B812FE53AA /* ImageCropper.podspec */, 176 | EF219414D7D69B9406560937 /* README.md */, 177 | 13D19B337353CD78EA001DE9 /* LICENSE */, 178 | ); 179 | name = "Podspec Metadata"; 180 | sourceTree = ""; 181 | }; 182 | 71592CF59DD4B8689B5F13B4 /* Pods */ = { 183 | isa = PBXGroup; 184 | children = ( 185 | 66A88AA3BF01F50AA421B4DF /* Pods-ImageCropper_Example.debug.xcconfig */, 186 | 9633BAF6CBDD8C7FCCF3D640 /* Pods-ImageCropper_Example.release.xcconfig */, 187 | 8AE21990F1E34AF0C59BE53C /* Pods-ImageCropper_Tests.debug.xcconfig */, 188 | B8EA65C942D40611F7D0021C /* Pods-ImageCropper_Tests.release.xcconfig */, 189 | ); 190 | name = Pods; 191 | sourceTree = ""; 192 | }; 193 | 9F1F943CBE7ADC137F2CA991 /* Frameworks */ = { 194 | isa = PBXGroup; 195 | children = ( 196 | 28CBDA345B9A672EECFE4D8D /* Pods_ImageCropper_Example.framework */, 197 | 76DFFA1EB8A1D517ADBA238E /* Pods_ImageCropper_Tests.framework */, 198 | ); 199 | name = Frameworks; 200 | sourceTree = ""; 201 | }; 202 | /* End PBXGroup section */ 203 | 204 | /* Begin PBXNativeTarget section */ 205 | 607FACCF1AFB9204008FA782 /* ImageCropper_Example */ = { 206 | isa = PBXNativeTarget; 207 | buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "ImageCropper_Example" */; 208 | buildPhases = ( 209 | 4181C45D54E9EDB90E8851E4 /* [CP] Check Pods Manifest.lock */, 210 | 607FACCC1AFB9204008FA782 /* Sources */, 211 | 607FACCD1AFB9204008FA782 /* Frameworks */, 212 | 607FACCE1AFB9204008FA782 /* Resources */, 213 | 28603CAAC3EF89A96270C8EF /* [CP] Embed Pods Frameworks */, 214 | ); 215 | buildRules = ( 216 | ); 217 | dependencies = ( 218 | ); 219 | name = ImageCropper_Example; 220 | productName = ImageCropper; 221 | productReference = 607FACD01AFB9204008FA782 /* ImageCropper_Example.app */; 222 | productType = "com.apple.product-type.application"; 223 | }; 224 | 607FACE41AFB9204008FA782 /* ImageCropper_Tests */ = { 225 | isa = PBXNativeTarget; 226 | buildConfigurationList = 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "ImageCropper_Tests" */; 227 | buildPhases = ( 228 | 7412F09A84BB804F73E1BA8C /* [CP] Check Pods Manifest.lock */, 229 | 607FACE11AFB9204008FA782 /* Sources */, 230 | 607FACE21AFB9204008FA782 /* Frameworks */, 231 | 607FACE31AFB9204008FA782 /* Resources */, 232 | ); 233 | buildRules = ( 234 | ); 235 | dependencies = ( 236 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */, 237 | ); 238 | name = ImageCropper_Tests; 239 | productName = Tests; 240 | productReference = 607FACE51AFB9204008FA782 /* ImageCropper_Tests.xctest */; 241 | productType = "com.apple.product-type.bundle.unit-test"; 242 | }; 243 | /* End PBXNativeTarget section */ 244 | 245 | /* Begin PBXProject section */ 246 | 607FACC81AFB9204008FA782 /* Project object */ = { 247 | isa = PBXProject; 248 | attributes = { 249 | LastSwiftUpdateCheck = 0830; 250 | LastUpgradeCheck = 0940; 251 | ORGANIZATIONNAME = CocoaPods; 252 | TargetAttributes = { 253 | 607FACCF1AFB9204008FA782 = { 254 | CreatedOnToolsVersion = 6.3.1; 255 | LastSwiftMigration = 0900; 256 | }; 257 | 607FACE41AFB9204008FA782 = { 258 | CreatedOnToolsVersion = 6.3.1; 259 | LastSwiftMigration = 0900; 260 | TestTargetID = 607FACCF1AFB9204008FA782; 261 | }; 262 | }; 263 | }; 264 | buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "ImageCropper" */; 265 | compatibilityVersion = "Xcode 3.2"; 266 | developmentRegion = English; 267 | hasScannedForEncodings = 0; 268 | knownRegions = ( 269 | en, 270 | Base, 271 | ); 272 | mainGroup = 607FACC71AFB9204008FA782; 273 | productRefGroup = 607FACD11AFB9204008FA782 /* Products */; 274 | projectDirPath = ""; 275 | projectRoot = ""; 276 | targets = ( 277 | 607FACCF1AFB9204008FA782 /* ImageCropper_Example */, 278 | 607FACE41AFB9204008FA782 /* ImageCropper_Tests */, 279 | ); 280 | }; 281 | /* End PBXProject section */ 282 | 283 | /* Begin PBXResourcesBuildPhase section */ 284 | 607FACCE1AFB9204008FA782 /* Resources */ = { 285 | isa = PBXResourcesBuildPhase; 286 | buildActionMask = 2147483647; 287 | files = ( 288 | 1B6CBB4920D8D67D003E4136 /* PostProduction.storyboard in Resources */, 289 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */, 290 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */, 291 | 1B6CBB4420D8D67D003E4136 /* Main.storyboard in Resources */, 292 | ); 293 | runOnlyForDeploymentPostprocessing = 0; 294 | }; 295 | 607FACE31AFB9204008FA782 /* Resources */ = { 296 | isa = PBXResourcesBuildPhase; 297 | buildActionMask = 2147483647; 298 | files = ( 299 | ); 300 | runOnlyForDeploymentPostprocessing = 0; 301 | }; 302 | /* End PBXResourcesBuildPhase section */ 303 | 304 | /* Begin PBXShellScriptBuildPhase section */ 305 | 28603CAAC3EF89A96270C8EF /* [CP] Embed Pods Frameworks */ = { 306 | isa = PBXShellScriptBuildPhase; 307 | buildActionMask = 2147483647; 308 | files = ( 309 | ); 310 | inputPaths = ( 311 | "${SRCROOT}/Pods/Target Support Files/Pods-ImageCropper_Example/Pods-ImageCropper_Example-frameworks.sh", 312 | "${BUILT_PRODUCTS_DIR}/ImageCropper/ImageCropper.framework", 313 | ); 314 | name = "[CP] Embed Pods Frameworks"; 315 | outputPaths = ( 316 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ImageCropper.framework", 317 | ); 318 | runOnlyForDeploymentPostprocessing = 0; 319 | shellPath = /bin/sh; 320 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ImageCropper_Example/Pods-ImageCropper_Example-frameworks.sh\"\n"; 321 | showEnvVarsInLog = 0; 322 | }; 323 | 4181C45D54E9EDB90E8851E4 /* [CP] Check Pods Manifest.lock */ = { 324 | isa = PBXShellScriptBuildPhase; 325 | buildActionMask = 2147483647; 326 | files = ( 327 | ); 328 | inputPaths = ( 329 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 330 | "${PODS_ROOT}/Manifest.lock", 331 | ); 332 | name = "[CP] Check Pods Manifest.lock"; 333 | outputPaths = ( 334 | "$(DERIVED_FILE_DIR)/Pods-ImageCropper_Example-checkManifestLockResult.txt", 335 | ); 336 | runOnlyForDeploymentPostprocessing = 0; 337 | shellPath = /bin/sh; 338 | 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"; 339 | showEnvVarsInLog = 0; 340 | }; 341 | 7412F09A84BB804F73E1BA8C /* [CP] Check Pods Manifest.lock */ = { 342 | isa = PBXShellScriptBuildPhase; 343 | buildActionMask = 2147483647; 344 | files = ( 345 | ); 346 | inputPaths = ( 347 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 348 | "${PODS_ROOT}/Manifest.lock", 349 | ); 350 | name = "[CP] Check Pods Manifest.lock"; 351 | outputPaths = ( 352 | "$(DERIVED_FILE_DIR)/Pods-ImageCropper_Tests-checkManifestLockResult.txt", 353 | ); 354 | runOnlyForDeploymentPostprocessing = 0; 355 | shellPath = /bin/sh; 356 | 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"; 357 | showEnvVarsInLog = 0; 358 | }; 359 | /* End PBXShellScriptBuildPhase section */ 360 | 361 | /* Begin PBXSourcesBuildPhase section */ 362 | 607FACCC1AFB9204008FA782 /* Sources */ = { 363 | isa = PBXSourcesBuildPhase; 364 | buildActionMask = 2147483647; 365 | files = ( 366 | 1B6CBB4720D8D67D003E4136 /* MainRouter.swift in Sources */, 367 | 1B6CBB4D20D8D67D003E4136 /* PostProductionViewController.swift in Sources */, 368 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, 369 | 1B6CBB4820D8D67D003E4136 /* MainViewController.swift in Sources */, 370 | 1B6CBB4520D8D67D003E4136 /* MainConfigurator.swift in Sources */, 371 | 1B6CBB4C20D8D67D003E4136 /* PostProductionRouter.swift in Sources */, 372 | 1B6CBB4B20D8D67D003E4136 /* PostProductionPresenter.swift in Sources */, 373 | 1B6CBB4A20D8D67D003E4136 /* PostProductionConfigurator.swift in Sources */, 374 | 1B6CBB4620D8D67D003E4136 /* MainPresenter.swift in Sources */, 375 | ); 376 | runOnlyForDeploymentPostprocessing = 0; 377 | }; 378 | 607FACE11AFB9204008FA782 /* Sources */ = { 379 | isa = PBXSourcesBuildPhase; 380 | buildActionMask = 2147483647; 381 | files = ( 382 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */, 383 | ); 384 | runOnlyForDeploymentPostprocessing = 0; 385 | }; 386 | /* End PBXSourcesBuildPhase section */ 387 | 388 | /* Begin PBXTargetDependency section */ 389 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */ = { 390 | isa = PBXTargetDependency; 391 | target = 607FACCF1AFB9204008FA782 /* ImageCropper_Example */; 392 | targetProxy = 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */; 393 | }; 394 | /* End PBXTargetDependency section */ 395 | 396 | /* Begin PBXVariantGroup section */ 397 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */ = { 398 | isa = PBXVariantGroup; 399 | children = ( 400 | 607FACDF1AFB9204008FA782 /* Base */, 401 | ); 402 | name = LaunchScreen.xib; 403 | sourceTree = ""; 404 | }; 405 | /* End PBXVariantGroup section */ 406 | 407 | /* Begin XCBuildConfiguration section */ 408 | 607FACED1AFB9204008FA782 /* Debug */ = { 409 | isa = XCBuildConfiguration; 410 | buildSettings = { 411 | ALWAYS_SEARCH_USER_PATHS = NO; 412 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 413 | CLANG_CXX_LIBRARY = "libc++"; 414 | CLANG_ENABLE_MODULES = YES; 415 | CLANG_ENABLE_OBJC_ARC = YES; 416 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 417 | CLANG_WARN_BOOL_CONVERSION = YES; 418 | CLANG_WARN_COMMA = YES; 419 | CLANG_WARN_CONSTANT_CONVERSION = YES; 420 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 421 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 422 | CLANG_WARN_EMPTY_BODY = YES; 423 | CLANG_WARN_ENUM_CONVERSION = YES; 424 | CLANG_WARN_INFINITE_RECURSION = YES; 425 | CLANG_WARN_INT_CONVERSION = YES; 426 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 427 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 428 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 429 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 430 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 431 | CLANG_WARN_STRICT_PROTOTYPES = YES; 432 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 433 | CLANG_WARN_UNREACHABLE_CODE = YES; 434 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 435 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 436 | COPY_PHASE_STRIP = NO; 437 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 438 | ENABLE_STRICT_OBJC_MSGSEND = YES; 439 | ENABLE_TESTABILITY = YES; 440 | GCC_C_LANGUAGE_STANDARD = gnu99; 441 | GCC_DYNAMIC_NO_PIC = NO; 442 | GCC_NO_COMMON_BLOCKS = YES; 443 | GCC_OPTIMIZATION_LEVEL = 0; 444 | GCC_PREPROCESSOR_DEFINITIONS = ( 445 | "DEBUG=1", 446 | "$(inherited)", 447 | ); 448 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 449 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 450 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 451 | GCC_WARN_UNDECLARED_SELECTOR = YES; 452 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 453 | GCC_WARN_UNUSED_FUNCTION = YES; 454 | GCC_WARN_UNUSED_VARIABLE = YES; 455 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 456 | MTL_ENABLE_DEBUG_INFO = YES; 457 | ONLY_ACTIVE_ARCH = YES; 458 | SDKROOT = iphoneos; 459 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 460 | }; 461 | name = Debug; 462 | }; 463 | 607FACEE1AFB9204008FA782 /* Release */ = { 464 | isa = XCBuildConfiguration; 465 | buildSettings = { 466 | ALWAYS_SEARCH_USER_PATHS = NO; 467 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 468 | CLANG_CXX_LIBRARY = "libc++"; 469 | CLANG_ENABLE_MODULES = YES; 470 | CLANG_ENABLE_OBJC_ARC = YES; 471 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 472 | CLANG_WARN_BOOL_CONVERSION = YES; 473 | CLANG_WARN_COMMA = YES; 474 | CLANG_WARN_CONSTANT_CONVERSION = YES; 475 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 476 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 477 | CLANG_WARN_EMPTY_BODY = YES; 478 | CLANG_WARN_ENUM_CONVERSION = YES; 479 | CLANG_WARN_INFINITE_RECURSION = YES; 480 | CLANG_WARN_INT_CONVERSION = YES; 481 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 482 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 483 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 484 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 485 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 486 | CLANG_WARN_STRICT_PROTOTYPES = YES; 487 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 488 | CLANG_WARN_UNREACHABLE_CODE = YES; 489 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 490 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 491 | COPY_PHASE_STRIP = NO; 492 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 493 | ENABLE_NS_ASSERTIONS = NO; 494 | ENABLE_STRICT_OBJC_MSGSEND = YES; 495 | GCC_C_LANGUAGE_STANDARD = gnu99; 496 | GCC_NO_COMMON_BLOCKS = YES; 497 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 498 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 499 | GCC_WARN_UNDECLARED_SELECTOR = YES; 500 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 501 | GCC_WARN_UNUSED_FUNCTION = YES; 502 | GCC_WARN_UNUSED_VARIABLE = YES; 503 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 504 | MTL_ENABLE_DEBUG_INFO = NO; 505 | SDKROOT = iphoneos; 506 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 507 | VALIDATE_PRODUCT = YES; 508 | }; 509 | name = Release; 510 | }; 511 | 607FACF01AFB9204008FA782 /* Debug */ = { 512 | isa = XCBuildConfiguration; 513 | baseConfigurationReference = 66A88AA3BF01F50AA421B4DF /* Pods-ImageCropper_Example.debug.xcconfig */; 514 | buildSettings = { 515 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 516 | INFOPLIST_FILE = ImageCropper/Info.plist; 517 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 518 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 519 | MODULE_NAME = ExampleApp; 520 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 521 | PRODUCT_NAME = "$(TARGET_NAME)"; 522 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 523 | SWIFT_VERSION = 4.0; 524 | }; 525 | name = Debug; 526 | }; 527 | 607FACF11AFB9204008FA782 /* Release */ = { 528 | isa = XCBuildConfiguration; 529 | baseConfigurationReference = 9633BAF6CBDD8C7FCCF3D640 /* Pods-ImageCropper_Example.release.xcconfig */; 530 | buildSettings = { 531 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 532 | INFOPLIST_FILE = ImageCropper/Info.plist; 533 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 534 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 535 | MODULE_NAME = ExampleApp; 536 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 537 | PRODUCT_NAME = "$(TARGET_NAME)"; 538 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 539 | SWIFT_VERSION = 4.0; 540 | }; 541 | name = Release; 542 | }; 543 | 607FACF31AFB9204008FA782 /* Debug */ = { 544 | isa = XCBuildConfiguration; 545 | baseConfigurationReference = 8AE21990F1E34AF0C59BE53C /* Pods-ImageCropper_Tests.debug.xcconfig */; 546 | buildSettings = { 547 | FRAMEWORK_SEARCH_PATHS = ( 548 | "$(SDKROOT)/Developer/Library/Frameworks", 549 | "$(inherited)", 550 | ); 551 | GCC_PREPROCESSOR_DEFINITIONS = ( 552 | "DEBUG=1", 553 | "$(inherited)", 554 | ); 555 | INFOPLIST_FILE = Tests/Info.plist; 556 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 557 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 558 | PRODUCT_NAME = "$(TARGET_NAME)"; 559 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 560 | SWIFT_VERSION = 4.0; 561 | }; 562 | name = Debug; 563 | }; 564 | 607FACF41AFB9204008FA782 /* Release */ = { 565 | isa = XCBuildConfiguration; 566 | baseConfigurationReference = B8EA65C942D40611F7D0021C /* Pods-ImageCropper_Tests.release.xcconfig */; 567 | buildSettings = { 568 | FRAMEWORK_SEARCH_PATHS = ( 569 | "$(SDKROOT)/Developer/Library/Frameworks", 570 | "$(inherited)", 571 | ); 572 | INFOPLIST_FILE = Tests/Info.plist; 573 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 574 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 575 | PRODUCT_NAME = "$(TARGET_NAME)"; 576 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 577 | SWIFT_VERSION = 4.0; 578 | }; 579 | name = Release; 580 | }; 581 | /* End XCBuildConfiguration section */ 582 | 583 | /* Begin XCConfigurationList section */ 584 | 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "ImageCropper" */ = { 585 | isa = XCConfigurationList; 586 | buildConfigurations = ( 587 | 607FACED1AFB9204008FA782 /* Debug */, 588 | 607FACEE1AFB9204008FA782 /* Release */, 589 | ); 590 | defaultConfigurationIsVisible = 0; 591 | defaultConfigurationName = Release; 592 | }; 593 | 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "ImageCropper_Example" */ = { 594 | isa = XCConfigurationList; 595 | buildConfigurations = ( 596 | 607FACF01AFB9204008FA782 /* Debug */, 597 | 607FACF11AFB9204008FA782 /* Release */, 598 | ); 599 | defaultConfigurationIsVisible = 0; 600 | defaultConfigurationName = Release; 601 | }; 602 | 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "ImageCropper_Tests" */ = { 603 | isa = XCConfigurationList; 604 | buildConfigurations = ( 605 | 607FACF31AFB9204008FA782 /* Debug */, 606 | 607FACF41AFB9204008FA782 /* Release */, 607 | ); 608 | defaultConfigurationIsVisible = 0; 609 | defaultConfigurationName = Release; 610 | }; 611 | /* End XCConfigurationList section */ 612 | }; 613 | rootObject = 607FACC81AFB9204008FA782 /* Project object */; 614 | } 615 | -------------------------------------------------------------------------------- /Example/ImageCropper.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/ImageCropper.xcodeproj/xcshareddata/xcschemes/ImageCropper-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 80 | 86 | 87 | 88 | 89 | 90 | 91 | 97 | 99 | 105 | 106 | 107 | 108 | 110 | 111 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /Example/ImageCropper.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/ImageCropper.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/ImageCropper/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ImageCropper 4 | // NickKopilovskii.NKAnimation 5 | // Created by Nick Kopilovskii on 06/06/2018. 6 | // Copyright (c) 2018 Nick Kopilovskii. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | return true 19 | } 20 | 21 | } 22 | 23 | -------------------------------------------------------------------------------- /Example/ImageCropper/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Example/ImageCropper/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Example/ImageCropper/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/ImageCropper/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSPhotoLibraryUsageDescription 26 | Gallery 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example/ImageCropper/Main/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 61 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 80 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 102 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 124 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 146 | 153 | 154 | 155 | 156 | 157 | 158 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | -------------------------------------------------------------------------------- /Example/ImageCropper/Main/MainConfigurator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ⚡️Created by Generatus⚡️ on 28.05.2018 3 | // 4 | // MainConfigurator.swift 5 | // 6 | // Created by NickKopilovskii 7 | // Copyright © NickKopilovskii. All rights reserved. 8 | // 9 | 10 | import Foundation 11 | 12 | protocol MainConfigurator { 13 | 14 | static func configure(for view: MainViewController) 15 | 16 | } 17 | 18 | class MainConfiguratorImplementation { 19 | 20 | } 21 | 22 | extension MainConfiguratorImplementation: MainConfigurator { 23 | 24 | static func configure(for view: MainViewController) { 25 | 26 | let router = MainRouterImplementation(for: view) 27 | 28 | let presenter = MainPresenterImplementation(for: view, with: router) 29 | view.presenter = presenter 30 | 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Example/ImageCropper/Main/MainPresenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ⚡️Created by Generatus⚡️ on 28.05.2018 3 | // 4 | // MainPresenter.swift 5 | // 6 | // Created by NickKopilovskii 7 | // Copyright © NickKopilovskii. All rights reserved. 8 | // 9 | 10 | import Foundation 11 | import ImageCropper 12 | 13 | protocol MainView: class { 14 | var cornerRadius: CGFloat? { get } 15 | func isBtnsFiguresEnable(_ enable:Bool) 16 | } 17 | 18 | protocol MainPresenter { 19 | 20 | func viewDidLoad() 21 | func getImage() 22 | func didSelect(_ image: Data) 23 | func cropFigure(_ figureID: Int) 24 | 25 | } 26 | 27 | protocol MainRouter { 28 | func openPhotoLibrary() 29 | func closePhotoLibrary() 30 | func openCropper(with figure: ImageCropperConfiguration.ImageCropperFigureType, image: Data, cornerRadius: CGFloat?) 31 | func showAlertNoImage() 32 | } 33 | 34 | class MainPresenterImplementation { 35 | 36 | private weak var view: MainView? 37 | 38 | private let router: MainRouter 39 | 40 | private var imageData: Data? 41 | //MARK: - 42 | 43 | init(for view: MainView, with router: MainRouter) { 44 | 45 | self.view = view 46 | self.router = router 47 | 48 | } 49 | 50 | } 51 | 52 | //MARK: - MainPresenter 53 | 54 | extension MainPresenterImplementation: MainPresenter { 55 | 56 | func viewDidLoad() { 57 | view?.isBtnsFiguresEnable(false) 58 | } 59 | 60 | func getImage() { 61 | router.openPhotoLibrary() 62 | } 63 | 64 | func didSelect(_ image: Data) { 65 | imageData = image 66 | router.closePhotoLibrary() 67 | view?.isBtnsFiguresEnable(true) 68 | } 69 | 70 | func cropFigure(_ figureID: Int) { 71 | 72 | guard let figure = ImageCropperConfiguration.ImageCropperFigureType(rawValue: figureID) else { return } 73 | 74 | guard let img = imageData else { 75 | router.showAlertNoImage() 76 | return 77 | } 78 | router.openCropper(with: figure, image: img, cornerRadius: view?.cornerRadius) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Example/ImageCropper/Main/MainRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ⚡️Created by Generatus⚡️ on 28.05.2018 3 | // 4 | // MainRouter.swift 5 | // 6 | // Created by NickKopilovskii 7 | // Copyright © NickKopilovskii. All rights reserved. 8 | // 9 | 10 | import UIKit 11 | import ImageCropper 12 | 13 | class MainRouterImplementation: NSObject { 14 | 15 | private weak var view: MainViewController? 16 | private var imagePicker: UIImagePickerController? 17 | 18 | init(for view: MainViewController) { 19 | self.view = view 20 | } 21 | 22 | } 23 | 24 | //MARK: - MainRouter 25 | 26 | extension MainRouterImplementation: MainRouter { 27 | 28 | func openPhotoLibrary() { 29 | guard let vc = view else { return } 30 | 31 | imagePicker = UIImagePickerController() 32 | imagePicker?.modalPresentationStyle = .popover 33 | imagePicker?.popoverPresentationController?.sourceView = vc.view 34 | imagePicker?.delegate = vc 35 | imagePicker?.sourceType = .photoLibrary 36 | vc.present(imagePicker!, animated: true, completion: nil) 37 | } 38 | 39 | func closePhotoLibrary() { 40 | guard let picker = imagePicker else { return } 41 | picker.dismiss(animated: true) 42 | } 43 | 44 | func openCropper(with figure: ImageCropperConfiguration.ImageCropperFigureType, image: Data, cornerRadius: CGFloat?) { 45 | guard let img = UIImage(data: image) else { return } 46 | var config = ImageCropperConfiguration(with: img, and: figure, cornerRadius: cornerRadius) 47 | config.showGrid = true 48 | if figure == .customRect { 49 | config.customRatio = CGSize(width: 6, height: 5) 50 | } 51 | 52 | config.backTintColor = .black 53 | config.backTitle = "" 54 | 55 | var croppedImage = img 56 | let cropper = ImageCropperViewController.initialize(with: config, completionHandler: { _croppedImage in 57 | guard let _img = _croppedImage else { return } 58 | croppedImage = _img 59 | }) { 60 | self.openPostProduction(with: croppedImage) 61 | } 62 | 63 | view?.navigationController?.pushViewController(cropper, animated: true) 64 | } 65 | 66 | func showAlertNoImage() { 67 | let alert = UIAlertController(title: "Error", message: "Please, select image!", preferredStyle: .alert) 68 | let ok = UIAlertAction(title: "OK", style: .destructive) { action in 69 | alert.dismiss(animated: true, completion: nil) 70 | } 71 | alert.addAction(ok) 72 | view?.present(alert, animated: true, completion: nil) 73 | return 74 | 75 | } 76 | 77 | func openPostProduction(with image:UIImage) { 78 | guard let result = UIStoryboard(name: "PostProduction", bundle: nil).instantiateInitialViewController() as? PostProductionViewController else { return} 79 | 80 | result.image = image 81 | view?.navigationController?.pushViewController(result, animated: true) 82 | } 83 | 84 | } 85 | 86 | -------------------------------------------------------------------------------- /Example/ImageCropper/Main/MainViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ⚡️Created by Generatus⚡️ on 28.05.2018 3 | // 4 | // MainViewController.swift 5 | // 6 | // Created by NickKopilovskii 7 | // Copyright © NickKopilovskii. All rights reserved. 8 | // 9 | 10 | import UIKit 11 | 12 | class MainViewController: UIViewController { 13 | 14 | var presenter: MainPresenter? 15 | 16 | @IBOutlet private weak var imgPreview: UIImageView! 17 | @IBOutlet private weak var txtCornerRadius: UITextField! 18 | @IBOutlet private var btnsFigures: [UIButton]! 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | MainConfiguratorImplementation.configure(for: self) 23 | presenter?.viewDidLoad() 24 | 25 | let doneToolbar: UIToolbar = UIToolbar(frame: CGRect.init(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 50)) 26 | doneToolbar.barStyle = .default 27 | 28 | let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) 29 | let done: UIBarButtonItem = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(doneButtonAction)) 30 | 31 | let items = [flexSpace, done] 32 | doneToolbar.items = items 33 | doneToolbar.sizeToFit() 34 | 35 | txtCornerRadius.inputAccessoryView = doneToolbar 36 | } 37 | 38 | @IBAction func btnPressed(_ sender: UIButton) { 39 | presenter?.cropFigure(sender.tag) 40 | } 41 | 42 | @IBAction func btnGetImagePressed(_ sender: UIButton) { 43 | presenter?.getImage() 44 | } 45 | 46 | @objc private func doneButtonAction() { 47 | txtCornerRadius.resignFirstResponder() 48 | } 49 | } 50 | 51 | //MARK: - MainView 52 | extension MainViewController: MainView { 53 | var cornerRadius: CGFloat? { 54 | guard let str = txtCornerRadius.text, let number = NumberFormatter().number(from: str) else { return nil } 55 | return CGFloat(truncating: number) 56 | } 57 | 58 | func isBtnsFiguresEnable(_ enable: Bool) { 59 | btnsFigures.forEach { btn in 60 | btn.isEnabled = enable 61 | } 62 | } 63 | } 64 | 65 | //MARK: - UIImagePickerControllerDelegate 66 | extension MainViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { 67 | func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { 68 | guard let image = info[UIImagePickerControllerOriginalImage] as? UIImage, let imageData = UIImagePNGRepresentation(image) else { 69 | return 70 | } 71 | imgPreview.image = image 72 | presenter?.didSelect(imageData) 73 | } 74 | } 75 | 76 | 77 | //MARK: - UIImagePickerControllerDelegate 78 | extension MainViewController: UITextFieldDelegate { 79 | 80 | } 81 | -------------------------------------------------------------------------------- /Example/ImageCropper/PostProduction/PostProduction.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 35 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Example/ImageCropper/PostProduction/PostProductionConfigurator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ⚡️Created by Generatus⚡️ on 29.05.2018 3 | // 4 | // PostProductionConfigurator.swift 5 | // 6 | // Created by NickKopilovskii 7 | // Copyright © NickKopilovskii. All rights reserved. 8 | // 9 | 10 | import Foundation 11 | 12 | protocol PostProductionConfigurator { 13 | 14 | static func configure(for view: PostProductionViewController) 15 | 16 | } 17 | 18 | class PostProductionConfiguratorImplementation { 19 | 20 | } 21 | 22 | extension PostProductionConfiguratorImplementation: PostProductionConfigurator { 23 | 24 | static func configure(for view: PostProductionViewController) { 25 | 26 | let router = PostProductionRouterImplementation(for: view) 27 | 28 | let presenter = PostProductionPresenterImplementation(for: view, with: router) 29 | view.presenter = presenter 30 | 31 | } 32 | 33 | } 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Example/ImageCropper/PostProduction/PostProductionPresenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ⚡️Created by Generatus⚡️ on 29.05.2018 3 | // 4 | // PostProductionPresenter.swift 5 | // 6 | // Created by NickKopilovskii 7 | // Copyright © NickKopilovskii. All rights reserved. 8 | // 9 | 10 | import Foundation 11 | 12 | protocol PostProductionView: class { 13 | 14 | } 15 | 16 | protocol PostProductionPresenter { 17 | 18 | func viewDidLoad() 19 | func back() 20 | func done() 21 | } 22 | 23 | protocol PostProductionRouter { 24 | func backToPrevious() 25 | func goToNext() 26 | } 27 | 28 | class PostProductionPresenterImplementation { 29 | 30 | private weak var view: PostProductionView? 31 | 32 | private let router: PostProductionRouter 33 | 34 | //MARK: - 35 | 36 | init(for view: PostProductionView, with router: PostProductionRouter) { 37 | 38 | self.view = view 39 | self.router = router 40 | 41 | } 42 | 43 | } 44 | 45 | //MARK: - PostProductionPresenter 46 | 47 | extension PostProductionPresenterImplementation: PostProductionPresenter { 48 | 49 | func viewDidLoad() { 50 | 51 | } 52 | 53 | func back() { 54 | router.backToPrevious() 55 | } 56 | 57 | func done() { 58 | router.goToNext() 59 | } 60 | 61 | } 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Example/ImageCropper/PostProduction/PostProductionRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ⚡️Created by Generatus⚡️ on 29.05.2018 3 | // 4 | // PostProductionRouter.swift 5 | // 6 | // Created by NickKopilovskii 7 | // Copyright © NickKopilovskii. All rights reserved. 8 | // 9 | 10 | import UIKit 11 | 12 | class PostProductionRouterImplementation { 13 | 14 | private weak var view: PostProductionViewController? 15 | 16 | init(for view: PostProductionViewController) { 17 | self.view = view 18 | } 19 | 20 | } 21 | 22 | //MARK: - PostProductionRouter 23 | 24 | extension PostProductionRouterImplementation: PostProductionRouter { 25 | func backToPrevious() { 26 | guard let nc = view?.navigationController else { 27 | view?.dismiss(animated: true, completion: nil) 28 | return 29 | } 30 | nc.popViewController(animated: true) 31 | } 32 | 33 | func goToNext() { 34 | guard let nc = view?.navigationController else { 35 | view?.dismiss(animated: true, completion: nil) 36 | return 37 | } 38 | nc.popToRootViewController(animated: true) 39 | } 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /Example/ImageCropper/PostProduction/PostProductionViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ⚡️Created by Generatus⚡️ on 29.05.2018 3 | // 4 | // PostProductionViewController.swift 5 | // 6 | // Created by NickKopilovskii 7 | // Copyright © NickKopilovskii. All rights reserved. 8 | // 9 | 10 | import UIKit 11 | 12 | class PostProductionViewController: UIViewController { 13 | 14 | var presenter: PostProductionPresenter? 15 | 16 | var image: UIImage? 17 | 18 | 19 | @IBOutlet private weak var imgResult: UIImageView! 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | PostProductionConfiguratorImplementation.configure(for: self) 24 | presenter?.viewDidLoad() 25 | imgResult.image = image 26 | 27 | guard let width = image?.size.width, let height = image?.size.height else { return } 28 | let constraint = NSLayoutConstraint(item: imgResult, attribute: .width, relatedBy: .equal, toItem: imgResult, attribute: .height, multiplier: width / height, constant: 0) 29 | imgResult.addConstraint(constraint) 30 | imgResult.layer.borderWidth = 1 31 | imgResult.layer.borderColor = UIColor.black.cgColor 32 | } 33 | 34 | @IBAction func btnBackPressed(_ sender: Any) { 35 | presenter?.back() 36 | } 37 | 38 | @IBAction func btnDonePressed(_ sender: Any) { 39 | presenter?.done() 40 | 41 | } 42 | } 43 | 44 | //MARK: - PostProductionView 45 | 46 | extension PostProductionViewController: PostProductionView { 47 | 48 | } 49 | -------------------------------------------------------------------------------- /Example/ImageCropper/iPad_Landscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkopilovskii/ImageCropper/cb90b8e8531574545d614530f682e419fef47e46/Example/ImageCropper/iPad_Landscape.png -------------------------------------------------------------------------------- /Example/ImageCropper/iPad_Portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkopilovskii/ImageCropper/cb90b8e8531574545d614530f682e419fef47e46/Example/ImageCropper/iPad_Portrait.png -------------------------------------------------------------------------------- /Example/ImageCropper/iPhone_Landscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkopilovskii/ImageCropper/cb90b8e8531574545d614530f682e419fef47e46/Example/ImageCropper/iPhone_Landscape.png -------------------------------------------------------------------------------- /Example/ImageCropper/iPhone_Portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkopilovskii/ImageCropper/cb90b8e8531574545d614530f682e419fef47e46/Example/ImageCropper/iPhone_Portrait.png -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | target 'ImageCropper_Example' do 4 | pod 'ImageCropper', :path => '../' 5 | 6 | target 'ImageCropper_Tests' do 7 | inherit! :search_paths 8 | 9 | 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - ImageCropper (0.1.0) 3 | 4 | DEPENDENCIES: 5 | - ImageCropper (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | ImageCropper: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | ImageCropper: bde52776636ca74b7122e7d9463c1f07201bce75 13 | 14 | PODFILE CHECKSUM: 0b27b12d38bbf516eea81f58555c7e5071912ba5 15 | 16 | COCOAPODS: 1.5.0 17 | -------------------------------------------------------------------------------- /Example/Pods/Local Podspecs/ImageCropper.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ImageCropper", 3 | "version": "0.1.0", 4 | "summary": "A short description of ImageCropper.", 5 | "description": "TODO: Add long description of the pod here.", 6 | "homepage": "https://github.com/Nick Kopilovskii/ImageCropper", 7 | "license": { 8 | "type": "MIT", 9 | "file": "LICENSE" 10 | }, 11 | "authors": { 12 | "Nick Kopilovskii": "nikolay.k@powercode.us" 13 | }, 14 | "source": { 15 | "git": "https://github.com/Nick Kopilovskii/ImageCropper.git", 16 | "tag": "0.1.0" 17 | }, 18 | "platforms": { 19 | "ios": "8.0" 20 | }, 21 | "source_files": "ImageCropper/Classes/**/*" 22 | } 23 | -------------------------------------------------------------------------------- /Example/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - ImageCropper (0.1.0) 3 | 4 | DEPENDENCIES: 5 | - ImageCropper (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | ImageCropper: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | ImageCropper: bde52776636ca74b7122e7d9463c1f07201bce75 13 | 14 | PODFILE CHECKSUM: 0b27b12d38bbf516eea81f58555c7e5071912ba5 15 | 16 | COCOAPODS: 1.5.0 17 | -------------------------------------------------------------------------------- /Example/Pods/Pods.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Pods/Pods.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/ImageCropper/ImageCropper-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_ImageCropper : NSObject 3 | @end 4 | @implementation PodsDummy_ImageCropper 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/ImageCropper/ImageCropper-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/ImageCropper/ImageCropper-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double ImageCropperVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char ImageCropperVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/ImageCropper/ImageCropper.modulemap: -------------------------------------------------------------------------------- 1 | framework module ImageCropper { 2 | umbrella header "ImageCropper-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/ImageCropper/ImageCropper.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/ImageCropper 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/ImageCropper/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ImageCropper_Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ImageCropper_Example/Pods-ImageCropper_Example-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## ImageCropper 5 | 6 | Copyright (c) 2018 Nick Kopilovskii 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | Generated by CocoaPods - https://cocoapods.org 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ImageCropper_Example/Pods-ImageCropper_Example-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2018 Nick Kopilovskii <nikolay.k@powercode.us> 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | License 38 | MIT 39 | Title 40 | ImageCropper 41 | Type 42 | PSGroupSpecifier 43 | 44 | 45 | FooterText 46 | Generated by CocoaPods - https://cocoapods.org 47 | Title 48 | 49 | Type 50 | PSGroupSpecifier 51 | 52 | 53 | StringsTable 54 | Acknowledgements 55 | Title 56 | Acknowledgements 57 | 58 | 59 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ImageCropper_Example/Pods-ImageCropper_Example-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_ImageCropper_Example : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_ImageCropper_Example 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ImageCropper_Example/Pods-ImageCropper_Example-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 7 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # frameworks to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 13 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 14 | 15 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 16 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 17 | 18 | # Used as a return value for each invocation of `strip_invalid_archs` function. 19 | STRIP_BINARY_RETVAL=0 20 | 21 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 22 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 23 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 24 | 25 | # Copies and strips a vendored framework 26 | install_framework() 27 | { 28 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 29 | local source="${BUILT_PRODUCTS_DIR}/$1" 30 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 31 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 32 | elif [ -r "$1" ]; then 33 | local source="$1" 34 | fi 35 | 36 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 37 | 38 | if [ -L "${source}" ]; then 39 | echo "Symlinked..." 40 | source="$(readlink "${source}")" 41 | fi 42 | 43 | # Use filter instead of exclude so missing patterns don't throw errors. 44 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 45 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 46 | 47 | local basename 48 | basename="$(basename -s .framework "$1")" 49 | binary="${destination}/${basename}.framework/${basename}" 50 | if ! [ -r "$binary" ]; then 51 | binary="${destination}/${basename}" 52 | fi 53 | 54 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 55 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 56 | strip_invalid_archs "$binary" 57 | fi 58 | 59 | # Resign the code if required by the build settings to avoid unstable apps 60 | code_sign_if_enabled "${destination}/$(basename "$1")" 61 | 62 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 63 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 64 | local swift_runtime_libs 65 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 66 | for lib in $swift_runtime_libs; do 67 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 68 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 69 | code_sign_if_enabled "${destination}/${lib}" 70 | done 71 | fi 72 | } 73 | 74 | # Copies and strips a vendored dSYM 75 | install_dsym() { 76 | local source="$1" 77 | if [ -r "$source" ]; then 78 | # Copy the dSYM into a the targets temp dir. 79 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 80 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 81 | 82 | local basename 83 | basename="$(basename -s .framework.dSYM "$source")" 84 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 85 | 86 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 87 | if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then 88 | strip_invalid_archs "$binary" 89 | fi 90 | 91 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 92 | # Move the stripped file into its final destination. 93 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 94 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 95 | else 96 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 97 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 98 | fi 99 | fi 100 | } 101 | 102 | # Signs a framework with the provided identity 103 | code_sign_if_enabled() { 104 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 105 | # Use the current code_sign_identitiy 106 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 107 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 108 | 109 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 110 | code_sign_cmd="$code_sign_cmd &" 111 | fi 112 | echo "$code_sign_cmd" 113 | eval "$code_sign_cmd" 114 | fi 115 | } 116 | 117 | # Strip invalid architectures 118 | strip_invalid_archs() { 119 | binary="$1" 120 | # Get architectures for current target binary 121 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 122 | # Intersect them with the architectures we are building for 123 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 124 | # If there are no archs supported by this binary then warn the user 125 | if [[ -z "$intersected_archs" ]]; then 126 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 127 | STRIP_BINARY_RETVAL=0 128 | return 129 | fi 130 | stripped="" 131 | for arch in $binary_archs; do 132 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 133 | # Strip non-valid architectures in-place 134 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 135 | stripped="$stripped $arch" 136 | fi 137 | done 138 | if [[ "$stripped" ]]; then 139 | echo "Stripped $binary of architectures:$stripped" 140 | fi 141 | STRIP_BINARY_RETVAL=1 142 | } 143 | 144 | 145 | if [[ "$CONFIGURATION" == "Debug" ]]; then 146 | install_framework "${BUILT_PRODUCTS_DIR}/ImageCropper/ImageCropper.framework" 147 | fi 148 | if [[ "$CONFIGURATION" == "Release" ]]; then 149 | install_framework "${BUILT_PRODUCTS_DIR}/ImageCropper/ImageCropper.framework" 150 | fi 151 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 152 | wait 153 | fi 154 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ImageCropper_Example/Pods-ImageCropper_Example-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then 7 | # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # resources to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 13 | 14 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 15 | > "$RESOURCES_TO_COPY" 16 | 17 | XCASSET_FILES=() 18 | 19 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 20 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 21 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 22 | 23 | case "${TARGETED_DEVICE_FAMILY:-}" in 24 | 1,2) 25 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 26 | ;; 27 | 1) 28 | TARGET_DEVICE_ARGS="--target-device iphone" 29 | ;; 30 | 2) 31 | TARGET_DEVICE_ARGS="--target-device ipad" 32 | ;; 33 | 3) 34 | TARGET_DEVICE_ARGS="--target-device tv" 35 | ;; 36 | 4) 37 | TARGET_DEVICE_ARGS="--target-device watch" 38 | ;; 39 | *) 40 | TARGET_DEVICE_ARGS="--target-device mac" 41 | ;; 42 | esac 43 | 44 | install_resource() 45 | { 46 | if [[ "$1" = /* ]] ; then 47 | RESOURCE_PATH="$1" 48 | else 49 | RESOURCE_PATH="${PODS_ROOT}/$1" 50 | fi 51 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 52 | cat << EOM 53 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 54 | EOM 55 | exit 1 56 | fi 57 | case $RESOURCE_PATH in 58 | *.storyboard) 59 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 60 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 61 | ;; 62 | *.xib) 63 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 64 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 65 | ;; 66 | *.framework) 67 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 68 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 69 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 70 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 71 | ;; 72 | *.xcdatamodel) 73 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 74 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 75 | ;; 76 | *.xcdatamodeld) 77 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 78 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 79 | ;; 80 | *.xcmappingmodel) 81 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 82 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 83 | ;; 84 | *.xcassets) 85 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 86 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 87 | ;; 88 | *) 89 | echo "$RESOURCE_PATH" || true 90 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 91 | ;; 92 | esac 93 | } 94 | 95 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 96 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 97 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 98 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 99 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 100 | fi 101 | rm -f "$RESOURCES_TO_COPY" 102 | 103 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] 104 | then 105 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 106 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 107 | while read line; do 108 | if [[ $line != "${PODS_ROOT}*" ]]; then 109 | XCASSET_FILES+=("$line") 110 | fi 111 | done <<<"$OTHER_XCASSETS" 112 | 113 | if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then 114 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 115 | else 116 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_BUILD_DIR}/assetcatalog_generated_info.plist" 117 | fi 118 | fi 119 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ImageCropper_Example/Pods-ImageCropper_Example-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_ImageCropper_ExampleVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_ImageCropper_ExampleVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ImageCropper_Example/Pods-ImageCropper_Example.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/ImageCropper" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/ImageCropper/ImageCropper.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "ImageCropper" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ImageCropper_Example/Pods-ImageCropper_Example.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_ImageCropper_Example { 2 | umbrella header "Pods-ImageCropper_Example-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ImageCropper_Example/Pods-ImageCropper_Example.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/ImageCropper" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/ImageCropper/ImageCropper.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "ImageCropper" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ImageCropper_Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ImageCropper_Tests/Pods-ImageCropper_Tests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ImageCropper_Tests/Pods-ImageCropper_Tests-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ImageCropper_Tests/Pods-ImageCropper_Tests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_ImageCropper_Tests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_ImageCropper_Tests 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ImageCropper_Tests/Pods-ImageCropper_Tests-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 7 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # frameworks to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 13 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 14 | 15 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 16 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 17 | 18 | # Used as a return value for each invocation of `strip_invalid_archs` function. 19 | STRIP_BINARY_RETVAL=0 20 | 21 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 22 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 23 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 24 | 25 | # Copies and strips a vendored framework 26 | install_framework() 27 | { 28 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 29 | local source="${BUILT_PRODUCTS_DIR}/$1" 30 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 31 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 32 | elif [ -r "$1" ]; then 33 | local source="$1" 34 | fi 35 | 36 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 37 | 38 | if [ -L "${source}" ]; then 39 | echo "Symlinked..." 40 | source="$(readlink "${source}")" 41 | fi 42 | 43 | # Use filter instead of exclude so missing patterns don't throw errors. 44 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 45 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 46 | 47 | local basename 48 | basename="$(basename -s .framework "$1")" 49 | binary="${destination}/${basename}.framework/${basename}" 50 | if ! [ -r "$binary" ]; then 51 | binary="${destination}/${basename}" 52 | fi 53 | 54 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 55 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 56 | strip_invalid_archs "$binary" 57 | fi 58 | 59 | # Resign the code if required by the build settings to avoid unstable apps 60 | code_sign_if_enabled "${destination}/$(basename "$1")" 61 | 62 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 63 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 64 | local swift_runtime_libs 65 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 66 | for lib in $swift_runtime_libs; do 67 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 68 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 69 | code_sign_if_enabled "${destination}/${lib}" 70 | done 71 | fi 72 | } 73 | 74 | # Copies and strips a vendored dSYM 75 | install_dsym() { 76 | local source="$1" 77 | if [ -r "$source" ]; then 78 | # Copy the dSYM into a the targets temp dir. 79 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 80 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 81 | 82 | local basename 83 | basename="$(basename -s .framework.dSYM "$source")" 84 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 85 | 86 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 87 | if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then 88 | strip_invalid_archs "$binary" 89 | fi 90 | 91 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 92 | # Move the stripped file into its final destination. 93 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 94 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 95 | else 96 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 97 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 98 | fi 99 | fi 100 | } 101 | 102 | # Signs a framework with the provided identity 103 | code_sign_if_enabled() { 104 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 105 | # Use the current code_sign_identitiy 106 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 107 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 108 | 109 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 110 | code_sign_cmd="$code_sign_cmd &" 111 | fi 112 | echo "$code_sign_cmd" 113 | eval "$code_sign_cmd" 114 | fi 115 | } 116 | 117 | # Strip invalid architectures 118 | strip_invalid_archs() { 119 | binary="$1" 120 | # Get architectures for current target binary 121 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 122 | # Intersect them with the architectures we are building for 123 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 124 | # If there are no archs supported by this binary then warn the user 125 | if [[ -z "$intersected_archs" ]]; then 126 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 127 | STRIP_BINARY_RETVAL=0 128 | return 129 | fi 130 | stripped="" 131 | for arch in $binary_archs; do 132 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 133 | # Strip non-valid architectures in-place 134 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 135 | stripped="$stripped $arch" 136 | fi 137 | done 138 | if [[ "$stripped" ]]; then 139 | echo "Stripped $binary of architectures:$stripped" 140 | fi 141 | STRIP_BINARY_RETVAL=1 142 | } 143 | 144 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 145 | wait 146 | fi 147 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ImageCropper_Tests/Pods-ImageCropper_Tests-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then 7 | # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # resources to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 13 | 14 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 15 | > "$RESOURCES_TO_COPY" 16 | 17 | XCASSET_FILES=() 18 | 19 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 20 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 21 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 22 | 23 | case "${TARGETED_DEVICE_FAMILY:-}" in 24 | 1,2) 25 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 26 | ;; 27 | 1) 28 | TARGET_DEVICE_ARGS="--target-device iphone" 29 | ;; 30 | 2) 31 | TARGET_DEVICE_ARGS="--target-device ipad" 32 | ;; 33 | 3) 34 | TARGET_DEVICE_ARGS="--target-device tv" 35 | ;; 36 | 4) 37 | TARGET_DEVICE_ARGS="--target-device watch" 38 | ;; 39 | *) 40 | TARGET_DEVICE_ARGS="--target-device mac" 41 | ;; 42 | esac 43 | 44 | install_resource() 45 | { 46 | if [[ "$1" = /* ]] ; then 47 | RESOURCE_PATH="$1" 48 | else 49 | RESOURCE_PATH="${PODS_ROOT}/$1" 50 | fi 51 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 52 | cat << EOM 53 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 54 | EOM 55 | exit 1 56 | fi 57 | case $RESOURCE_PATH in 58 | *.storyboard) 59 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 60 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 61 | ;; 62 | *.xib) 63 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 64 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 65 | ;; 66 | *.framework) 67 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 68 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 69 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 70 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 71 | ;; 72 | *.xcdatamodel) 73 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 74 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 75 | ;; 76 | *.xcdatamodeld) 77 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 78 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 79 | ;; 80 | *.xcmappingmodel) 81 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 82 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 83 | ;; 84 | *.xcassets) 85 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 86 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 87 | ;; 88 | *) 89 | echo "$RESOURCE_PATH" || true 90 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 91 | ;; 92 | esac 93 | } 94 | 95 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 96 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 97 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 98 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 99 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 100 | fi 101 | rm -f "$RESOURCES_TO_COPY" 102 | 103 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] 104 | then 105 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 106 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 107 | while read line; do 108 | if [[ $line != "${PODS_ROOT}*" ]]; then 109 | XCASSET_FILES+=("$line") 110 | fi 111 | done <<<"$OTHER_XCASSETS" 112 | 113 | if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then 114 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 115 | else 116 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_BUILD_DIR}/assetcatalog_generated_info.plist" 117 | fi 118 | fi 119 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ImageCropper_Tests/Pods-ImageCropper_Tests-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_ImageCropper_TestsVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_ImageCropper_TestsVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ImageCropper_Tests/Pods-ImageCropper_Tests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/ImageCropper" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/ImageCropper/ImageCropper.framework/Headers" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ImageCropper_Tests/Pods-ImageCropper_Tests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_ImageCropper_Tests { 2 | umbrella header "Pods-ImageCropper_Tests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ImageCropper_Tests/Pods-ImageCropper_Tests.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/ImageCropper" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/ImageCropper/ImageCropper.framework/Headers" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Example/Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/Tests/Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import ImageCropper 3 | 4 | class Tests: XCTestCase { 5 | 6 | override func setUp() { 7 | super.setUp() 8 | // Put setup code here. This method is called before the invocation of each test method in the class. 9 | } 10 | 11 | override func tearDown() { 12 | // Put teardown code here. This method is called after the invocation of each test method in the class. 13 | super.tearDown() 14 | } 15 | 16 | func testExample() { 17 | // This is an example of a functional test case. 18 | XCTAssert(true, "Pass") 19 | } 20 | 21 | func testPerformanceExample() { 22 | // This is an example of a performance test case. 23 | self.measure() { 24 | // Put the code you want to measure the time of here. 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /ImageCropper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkopilovskii/ImageCropper/cb90b8e8531574545d614530f682e419fef47e46/ImageCropper.png -------------------------------------------------------------------------------- /ImageCropper.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint ImageCropper.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'ImageCropper' 11 | s.version = '0.1.5.1' 12 | s.summary = 'Module for implementing the process of cropping images' 13 | 14 | # This description is used to generate tags and improve search results. 15 | # * Think: What does it do? Why did you write it? What is the focus? 16 | # * Try to keep it short, snappy and to the point. 17 | # * Write the description between the DESC delimiters below. 18 | # * Finally, don't worry about the indent, CocoaPods strips it! 19 | 20 | s.description = <<-DESC 21 | Module for implementing the process of cropping images 22 | 23 | In the process of creating a variety of projects, developers often face the need to crop images (whether the user's avatar on the social network, background images, and so on). 24 | 25 | Of course, iOS provides its own tools for image processing using the "Photos" application, but its use is not always convenient, justified, or even possible. 26 | 27 | This library provides the ability to cut out sections of the original image in specified proportions by user's gesture interactions. 28 | 29 | This solution presents itself a module developed on the basis of MVP + Clean Architecture (https://github.com/FortechRomania/ios-mvp-clean-architecture/) by code generator Generatus (https://github.com/Ryasnoy/Generatus) 30 | DESC 31 | 32 | s.homepage = 'https://github.com/nkopilovskii/ImageCropper' 33 | s.screenshots = 'https://github.com/nkopilovskii/ImageCropper/ImageCropper.png' 34 | s.license = { :type => 'MIT', :file => 'LICENSE' } 35 | s.author = { 'Nick Kopilovskii' => 'nkopilovskii@gmail.com' } 36 | s.source = { :git => 'https://github.com/nkopilovskii/ImageCropper.git', :tag => s.version.to_s } 37 | # s.social_media_url = 'https://twitter.com/MKopilovskii' 38 | s.ios.deployment_target = '11.0' 39 | 40 | s.swift_version = '5.0' 41 | s.source_files = 'ImageCropper/Classes/**/*.{xib,swift}' 42 | 43 | # s.resource_bundles = { 44 | # 'ImageCropper' => ['ImageCropper/Assets/*.png'] 45 | # } 46 | 47 | # s.public_header_files = 'Pod/Classes/**/*.h' 48 | s.frameworks = 'UIKit' 49 | #'MapKit' 50 | # s.dependency 'AFNetworking', '~> 2.3' 51 | end 52 | -------------------------------------------------------------------------------- /ImageCropper/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkopilovskii/ImageCropper/cb90b8e8531574545d614530f682e419fef47e46/ImageCropper/Assets/.gitkeep -------------------------------------------------------------------------------- /ImageCropper/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkopilovskii/ImageCropper/cb90b8e8531574545d614530f682e419fef47e46/ImageCropper/Classes/.gitkeep -------------------------------------------------------------------------------- /ImageCropper/Classes/ImageCropper.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 62 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /ImageCropper/Classes/ImageCropperConfiguration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Nick Kopilovskii on 28.05.2018. 3 | // Copyright © 2018 Nick Kopilovskii. All rights reserved. 4 | // 5 | 6 | import UIKit 7 | 8 | public typealias ImageCropperCompletion = (UIImage?) -> Void 9 | public typealias ImageCropperDismiss = () -> Void 10 | 11 | 12 | public struct ImageCropperConfiguration { 13 | 14 | public enum ImageCropperFigureType: Int { 15 | case circle 16 | case square 17 | case rect2x1 18 | case rect1x2 19 | case rect4x3 20 | case rect3x4 21 | case rect16x9 22 | case rect9x16 23 | case customRect 24 | } 25 | 26 | var image: UIImage 27 | var figure: ImageCropperFigureType 28 | /** 29 | The parameter indicates the radius of the corners with respect to half the length of the smaller side of the rectangle. 30 | 31 | If input value greather 1, *cornerRadius == 1* 32 | 33 | If input value less 0, *cornerRadius == fabsf(inputValue)* 34 | 35 | If *figure == circle*, *cornerRadius == 1* 36 | 37 | default: 0 38 | */ 39 | var cornerRadius: CGFloat 40 | 41 | public var customRatio: CGSize? 42 | 43 | 44 | public var maskFillColor: UIColor? 45 | public var borderColor: UIColor? 46 | 47 | public var showGrid = false 48 | public var gridColor: UIColor? 49 | public var doneTitle: String? 50 | public var cancelTitle: String? 51 | 52 | public var backTitle: String? 53 | public var backImage: UIImage? 54 | public var backTintColor: UIColor? 55 | 56 | 57 | public init(with image: UIImage, and figure: ImageCropperFigureType, cornerRadius: CGFloat? = nil) { 58 | self.image = image.normalizeOrientation() 59 | self.figure = figure 60 | 61 | switch figure { 62 | case .circle: self.cornerRadius = 1 63 | default: 64 | guard let radius = cornerRadius else { 65 | self.cornerRadius = 0 66 | return 67 | } 68 | self.cornerRadius = radius > 1 ? 1 : CGFloat(fabsf(Float(radius))) 69 | } 70 | } 71 | 72 | } 73 | 74 | 75 | -------------------------------------------------------------------------------- /ImageCropper/Classes/ImageCropperConfigurator.swift: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // ImageCropperConfigurator.swift 4 | // 5 | // Created by NickKopilovskii 6 | // Copyright © NickKopilovskii. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol ImageCropperConfigurator { 12 | static func configure(for view: ImageCropperViewController, with configuration:ImageCropperConfiguration, completionHandler: @escaping ImageCropperCompletion) 13 | 14 | static func configure(for view: ImageCropperViewController, with configuration:ImageCropperConfiguration, completionHandler: @escaping ImageCropperCompletion, dismiss: @escaping ImageCropperDismiss) 15 | } 16 | 17 | class ImageCropperConfiguratorImplementation { } 18 | 19 | extension ImageCropperConfiguratorImplementation: ImageCropperConfigurator { 20 | 21 | static func configure(for view: ImageCropperViewController, with configuration:ImageCropperConfiguration, completionHandler: @escaping ImageCropperCompletion) { 22 | 23 | let router = ImageCropperRouterImplementation(for: view, with: completionHandler) 24 | 25 | let model = ImageCropperModelImplementation(with: configuration) 26 | 27 | let presenter = ImageCropperPresenterImplementation(for: view, with: router, and: model) 28 | view.presenter = presenter 29 | } 30 | 31 | static func configure(for view: ImageCropperViewController, with configuration:ImageCropperConfiguration, completionHandler: @escaping ImageCropperCompletion, dismiss: @escaping ImageCropperDismiss) { 32 | 33 | let router = ImageCropperRouterImplementation(for: view, with: completionHandler, dismiss: dismiss) 34 | 35 | let model = ImageCropperModelImplementation(with: configuration) 36 | 37 | let presenter = ImageCropperPresenterImplementation(for: view, with: router, and: model) 38 | view.presenter = presenter 39 | 40 | } 41 | 42 | } 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /ImageCropper/Classes/ImageCropperModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Nick Kopilovskii on 30.05.2018. 3 | // Copyright © 2018 Nick Kopilovskii. All rights reserved. 4 | // 5 | 6 | import UIKit 7 | 8 | class ImageCropperModelImplementation { 9 | 10 | fileprivate let configuration: ImageCropperConfiguration 11 | 12 | fileprivate var parentRect: CGRect? 13 | 14 | fileprivate var initialSize: CGSize? 15 | 16 | fileprivate var figureFrame: CGRect? 17 | fileprivate var panLastLocation: CGPoint? 18 | fileprivate var imageFrame = CGRect.zero 19 | fileprivate var gridSize:CGFloat? 20 | 21 | fileprivate var cornerRadius = CGFloat(0) 22 | 23 | 24 | init(with configuration: ImageCropperConfiguration) { 25 | self.configuration = configuration 26 | } 27 | } 28 | 29 | 30 | extension ImageCropperModelImplementation: ImageCropperModel { 31 | 32 | var image: UIImage { 33 | return configuration.image 34 | } 35 | 36 | var parentFrame: CGRect { 37 | get { 38 | return parentRect ?? .zero 39 | } 40 | set { 41 | parentRect = newValue 42 | 43 | let figureSize = configuration.figure.maskSize(with: newValue.size, ratio: configuration.customRatio) 44 | figureFrame = CGRect(x: (newValue.width - figureSize.width) / 2, y: (newValue.height - figureSize.height) / 2, width: figureSize.width, height: figureSize.height) 45 | cornerRadius = min(figureSize.width, figureSize.height) / 2 * configuration.cornerRadius 46 | 47 | imageFrame = imageInitialFrame 48 | } 49 | } 50 | 51 | var imageInitialFrame: CGRect { 52 | let figureSize = figureFrame?.size ?? .zero 53 | var size = image.size.scale(to: figureSize) 54 | size = CGSize(width: size.width * 1.25, height: size.height * 1.25) 55 | return CGRect(x: (parentFrame.width - size.width) / 2, y: (parentFrame.height - size.height) / 2, width: size.width, height: size.height) 56 | } 57 | 58 | var mask: CGPath { 59 | guard figureFrame != nil else { 60 | return UIBezierPath(rect: .zero).cgPath 61 | } 62 | let hole = UIBezierPath(cgPath: border) 63 | let path = UIBezierPath(roundedRect: parentFrame, cornerRadius: 0) 64 | path.append(hole) 65 | return path.cgPath 66 | } 67 | 68 | var fillColor: UIColor { 69 | if #available(iOS 10.0, *) { 70 | return configuration.maskFillColor ?? UIColor(displayP3Red: 0, green: 0, blue: 0, alpha: 0.5) 71 | } else { 72 | return configuration.maskFillColor ?? UIColor.init(red: 0, green: 0, blue: 0, alpha: 0.5) 73 | } 74 | } 75 | 76 | var border: CGPath { 77 | guard let frame = figureFrame else { return UIBezierPath(rect: .zero).cgPath } 78 | return UIBezierPath(roundedRect: frame, cornerRadius: cornerRadius != 0 ? cornerRadius : 1.0).cgPath 79 | } 80 | 81 | var borderColor: CGColor { 82 | return configuration.borderColor?.cgColor ?? UIColor.lightGray.cgColor 83 | } 84 | 85 | var grid: [CGPath] { 86 | guard configuration.showGrid else { return [CGPath]() } 87 | guard let frame = figureFrame else { return [CGPath]() } 88 | let step = configuration.figure == .customRect ? 0 : frame.width / CGFloat(configuration.figure.gridUnitDevider()) 89 | let parentFrame = self.parentFrame 90 | 91 | var lines = [CGPath]() 92 | 93 | /* drawin horizontal grid lines up from figure origin */ 94 | lines.append(contentsOf: NKGridBuilder.up.lines(from: frame.origin, in: parentFrame, with: step)) 95 | /* drawin vertical grid lines down from figure origin */ 96 | lines.append(contentsOf: NKGridBuilder.down.lines(from: frame.origin, in: parentFrame, with: step)) 97 | /* drawin vertical grid lines left from figure origin */ 98 | lines.append(contentsOf: NKGridBuilder.left.lines(from: frame.origin, in: parentFrame, with: step)) 99 | /* drawin vertical grid lines right from figure origin */ 100 | lines.append(contentsOf: NKGridBuilder.right.lines(from: frame.origin, in: parentFrame, with: step)) 101 | 102 | return lines 103 | } 104 | 105 | var gridColor: CGColor { 106 | if #available(iOS 10.0, *) { 107 | return configuration.gridColor?.cgColor ?? UIColor(displayP3Red: 1, green: 1, blue: 1, alpha: 0.5).cgColor 108 | } else { 109 | return configuration.gridColor?.cgColor ?? UIColor.init(red: 1, green: 1, blue: 1, alpha: 0.5).cgColor 110 | } 111 | } 112 | 113 | var doneTitle: String? { 114 | return configuration.doneTitle 115 | } 116 | 117 | var cancelTitle: String? { 118 | return configuration.cancelTitle 119 | } 120 | 121 | var backTitle: String? { 122 | return configuration.backTitle 123 | } 124 | 125 | var backImage: UIImage? { 126 | return configuration.backImage 127 | } 128 | 129 | var backTintColor: UIColor? { 130 | return configuration.backTintColor 131 | } 132 | 133 | func draggingFrame(for point: CGPoint) -> CGRect { 134 | let previousLocation = panLastLocation ?? point 135 | let difference = CGPoint(x: point.x - previousLocation.x, y: point.y - previousLocation.y) 136 | 137 | guard let borders = figureFrame else { return imageFrame } 138 | 139 | let x = imageFrame.origin.x + difference.x 140 | let newX = x < borders.origin.x && x + imageFrame.width > borders.maxX ? x : imageFrame.origin.x 141 | 142 | let y = imageFrame.origin.y + difference.y 143 | let newY = y < borders.origin.y && y + imageFrame.height > borders.maxY ? y : imageFrame.origin.y 144 | 145 | imageFrame = CGRect(origin: CGPoint(x: newX, y: newY), size: imageFrame.size) 146 | panLastLocation = point 147 | return imageFrame 148 | } 149 | 150 | func setStartedPinch() { 151 | initialSize = CGSize(width: imageFrame.width, height: imageFrame.height) 152 | } 153 | 154 | func scalingFrame(for scale: CGFloat) -> CGRect { 155 | let borders = figureFrame ?? .zero 156 | let pinchStartSize: CGSize 157 | if initialSize == nil { 158 | pinchStartSize = CGSize(width: imageFrame.width, height: imageFrame.height) 159 | } else { 160 | pinchStartSize = initialSize! 161 | } 162 | var newSize = CGSize(width: pinchStartSize.width * scale, height: pinchStartSize.height * scale) 163 | 164 | if newSize.width < borders.width || newSize.height < borders.height { 165 | newSize = image.size.scale(to: borders.size) 166 | } 167 | var newX = imageFrame.origin.x - (newSize.width - imageFrame.width) / 2 168 | var newY = imageFrame.origin.y - (newSize.height - imageFrame.height) / 2 169 | 170 | if newX + newSize.width <= borders.maxX { 171 | newX = borders.maxX - newSize.width 172 | } 173 | else if newX >= borders.origin.x { 174 | newX = borders.origin.x 175 | } 176 | 177 | if newY + newSize.height <= borders.maxY { 178 | newY = borders.maxY - newSize.height 179 | } 180 | else if newY >= borders.origin.y { 181 | newY = borders.origin.y 182 | } 183 | 184 | if newSize.width / image.size.width < 2 || newSize.height / image.size.height < 2 { 185 | imageFrame = CGRect(origin: CGPoint(x: newX, y: newY), size: newSize) 186 | } 187 | 188 | return imageFrame 189 | } 190 | 191 | func transformatingFinished() { 192 | panLastLocation = nil 193 | } 194 | 195 | func centerFrame() -> CGRect { 196 | if imageInitialFrame.center != imageFrame.center { 197 | imageFrame.center = imageInitialFrame.center 198 | } 199 | else { 200 | imageFrame = imageInitialFrame 201 | } 202 | 203 | return imageFrame 204 | } 205 | 206 | func crop() -> UIImage { 207 | guard let borders = figureFrame else { 208 | return image 209 | } 210 | let point = CGPoint(x: borders.origin.x - imageFrame.origin.x, y: borders.origin.y - imageFrame.origin.y) 211 | let frame = CGRect(origin: point, size: borders.size) 212 | let x = frame.origin.x * image.size.width / imageFrame.width 213 | let y = frame.origin.y * image.size.height / imageFrame.height 214 | let width = frame.width * image.size.width / imageFrame.width 215 | let height = frame.height * image.size.height / imageFrame.height 216 | let croppedRect = CGRect(x: x, y: y, width: width, height: height) 217 | guard let imageRef = image.cgImage?.cropping(to: croppedRect) else { 218 | return image 219 | } 220 | 221 | let croppedImage = UIImage(cgImage: imageRef) 222 | return configuration.cornerRadius != 0 ? cutCorners(for: croppedImage) : croppedImage 223 | 224 | } 225 | 226 | func cutCorners(for originalImage: UIImage) -> UIImage { 227 | let imgRect = CGRect(origin: .zero, size: originalImage.size) 228 | let cornerRadius = min(imgRect.width, imgRect.height) / 2 * configuration.cornerRadius 229 | let path = UIBezierPath(roundedRect: imgRect, cornerRadius: cornerRadius) 230 | 231 | UIGraphicsBeginImageContextWithOptions(originalImage.size, false, 0) 232 | path.addClip() 233 | originalImage.draw(at: .zero) 234 | let croppedImage = UIGraphicsGetImageFromCurrentImageContext() 235 | UIGraphicsEndImageContext() 236 | 237 | return croppedImage ?? originalImage 238 | } 239 | 240 | } 241 | 242 | extension CGRect { 243 | var center: CGPoint { 244 | set { 245 | origin.x = newValue.x - width / 2 246 | origin.y = newValue.y - height / 2 247 | } 248 | 249 | get { 250 | return CGPoint(x: origin.x + width / 2, y: origin.y + height / 2) 251 | } 252 | } 253 | } 254 | 255 | extension ImageCropperConfiguration.ImageCropperFigureType { 256 | fileprivate func maskSize(with parentSize:CGSize, ratio: CGSize?) -> CGSize { 257 | let parentVertical = parentSize.width < parentSize.height 258 | var width: CGFloat, height: CGFloat 259 | switch self { 260 | case .circle, .square: 261 | width = parentVertical ? parentSize.width * 0.75 : parentSize.height * 0.65 262 | height = width 263 | case .rect2x1: 264 | width = parentVertical ? parentSize.width * 0.6 : parentSize.width * 0.7 265 | height = width / 2 266 | case .rect1x2: 267 | height = parentSize.height * 0.6 268 | width = height / 2 269 | case .rect4x3: 270 | width = parentVertical ? parentSize.width * 0.6 : parentSize.height * 0.8 271 | height = width * 3 / 4 272 | case .rect3x4: 273 | height = parentSize.height * 0.6 274 | width = height * 3 / 4 275 | case .rect16x9: 276 | width = parentVertical ? parentSize.width * 0.8 : parentSize.width * 0.6 277 | height = width * 9 / 16 278 | case .rect9x16: 279 | height = parentSize.height * 0.7 280 | width = height * 9 / 16 281 | case .customRect: 282 | let customRatio = ratio ?? CGSize(width: 1, height: 1) //else { return ImageCropperConfiguration.ImageCropperFigureType.square.maskSize(with:parentSize) } 283 | if customRatio.width > customRatio.height { 284 | width = parentVertical ? parentSize.width * 0.8 : parentSize.width * 0.6 285 | height = width * customRatio.height / customRatio.width 286 | } 287 | else if customRatio.width < customRatio.height { 288 | height = parentSize.height * 0.7 289 | width = height * customRatio.width / customRatio.height 290 | } 291 | else { 292 | return ImageCropperConfiguration.ImageCropperFigureType.square.maskSize(with:parentSize, ratio: customRatio) 293 | } 294 | // fatalError() 295 | } 296 | 297 | return CGSize(width: width, height: height) 298 | } 299 | 300 | fileprivate func gridUnitDevider() -> Int { 301 | switch self { 302 | case .circle, .square, .rect2x1: 303 | return 2 304 | case .rect1x2: 305 | return 1 306 | case .rect4x3: 307 | return 4 308 | case .rect3x4: 309 | return 3 310 | case .rect16x9: 311 | return 16 312 | case .rect9x16: 313 | return 9 314 | case .customRect: 315 | return 0 316 | // fatalError() 317 | } 318 | } 319 | 320 | } 321 | 322 | 323 | fileprivate enum NKGridBuilder { 324 | case up, down, left, right 325 | 326 | private func delta(with step: CGFloat) -> CGPoint { 327 | switch self { 328 | case .up: return CGPoint(x: 0, y: -step) 329 | case .down: return CGPoint(x: 0, y: step) 330 | case .left: return CGPoint(x: -step, y: 0) 331 | case .right: return CGPoint(x: step, y: 0) 332 | } 333 | } 334 | 335 | private func lineStart(for point: CGPoint) -> CGPoint { 336 | switch self { 337 | case .up, .down: return CGPoint(x: 0, y: point.y) 338 | case .left, .right: return CGPoint(x: point.x, y: 0) 339 | } 340 | } 341 | 342 | private func lineEnd(for point: CGPoint, in size: CGSize) -> CGPoint { 343 | switch self { 344 | case .up, .down: return CGPoint(x: size.width, y: point.y) 345 | case .left, .right: return CGPoint(x: point.x, y: size.height) 346 | } 347 | } 348 | 349 | func lines(from point: CGPoint, in bounds: CGRect, with step: CGFloat) -> [CGPath] { 350 | guard step > 0 else { return [] } 351 | var lines = [CGPath]() 352 | let deltaPoint = delta(with: step) 353 | var benchmark = self == .up || self == .left ? CGPoint(x: point.x + deltaPoint.x, y: point.y + deltaPoint.y) : point 354 | 355 | while bounds.contains(benchmark) { 356 | let linePath = UIBezierPath() 357 | linePath.move(to: lineStart(for: benchmark)) 358 | linePath.addLine(to: lineEnd(for: benchmark, in: bounds.size)) 359 | lines.append(linePath.cgPath) 360 | 361 | benchmark = CGPoint(x: benchmark.x + deltaPoint.x, y: benchmark.y + deltaPoint.y) 362 | } 363 | 364 | return lines 365 | } 366 | } 367 | 368 | extension CGSize { 369 | func scale(to size: CGSize) -> CGSize { 370 | var newWidth: CGFloat 371 | var newHeight: CGFloat 372 | 373 | if width > height { 374 | newHeight = size.height 375 | newWidth = newHeight * width / height 376 | } else if width < height { 377 | newWidth = size.width 378 | newHeight = newWidth * height / width 379 | } else { 380 | newHeight = max(size.width, size.height) 381 | newWidth = newHeight 382 | } 383 | 384 | if newHeight < size.height { 385 | newHeight = size.height 386 | newWidth = newHeight * width / height 387 | } else if newWidth < size.width { 388 | newWidth = size.width 389 | newHeight = newWidth * height / width 390 | } 391 | return CGSize(width: newWidth, height: newHeight) //?? .zero 392 | } 393 | } 394 | -------------------------------------------------------------------------------- /ImageCropper/Classes/ImageCropperPresenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageCropperPresenter.swift 3 | // 4 | // Created by NickKopilovskii 5 | // Copyright © NickKopilovskii. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | 10 | 11 | protocol ImageCropperView: class { 12 | 13 | func set(_ image: UIImage) 14 | func setImageFrame(_ frame: CGRect) 15 | func transformImage(with frame: CGRect) 16 | func clearMask() 17 | func drawMask(by path: CGPath, with fillColor: UIColor) 18 | func clearBorderAndGrid() 19 | func drawBorber(by path: CGPath, with strokeColor: CGColor) 20 | func drawGrid(with lines: [CGPath], with strokeColor: CGColor) 21 | 22 | func setDone(_ title: String?) 23 | func setCancel(_ title: String?) 24 | func showBottomButtons(_ show: Bool) 25 | 26 | func setBackButton(title: String?, image: UIImage?, tintColor: UIColor?) 27 | 28 | func activityIndicator(_ show: Bool) 29 | } 30 | 31 | protocol ImageCropperPresenter { 32 | 33 | func viewDidLoad() 34 | func viewDidLayoutSubviews(in frame: CGRect) 35 | func userInteraction(_ takesPlace: Bool) 36 | func didDrag(to location: CGPoint) 37 | func didPinchStarted() 38 | func didScale(with scale: CGFloat) 39 | 40 | func centerImage() 41 | 42 | func crop() 43 | func cancel() 44 | } 45 | 46 | protocol ImageCropperRouter { 47 | func finish(with croppedImage:UIImage) 48 | func cancel() 49 | } 50 | 51 | protocol ImageCropperModel { 52 | var image: UIImage { get } 53 | var parentFrame: CGRect { set get } 54 | var imageInitialFrame: CGRect { get } 55 | 56 | var mask: CGPath { get } 57 | var fillColor: UIColor { get } 58 | 59 | var border: CGPath { get } 60 | var borderColor: CGColor { get } 61 | 62 | var grid: [CGPath] { get } 63 | var gridColor: CGColor { get } 64 | 65 | var doneTitle: String? { get } 66 | var cancelTitle: String? { get } 67 | 68 | var backTitle: String? { get } 69 | var backImage: UIImage? { get } 70 | var backTintColor: UIColor? { get } 71 | 72 | func draggingFrame(for point: CGPoint) -> CGRect 73 | func setStartedPinch() 74 | func scalingFrame(for scale: CGFloat) -> CGRect 75 | func transformatingFinished() 76 | func centerFrame() -> CGRect 77 | 78 | func crop() -> UIImage 79 | 80 | } 81 | 82 | class ImageCropperPresenterImplementation { 83 | 84 | fileprivate weak var view: ImageCropperView? 85 | 86 | fileprivate let router: ImageCropperRouter 87 | 88 | fileprivate var model: ImageCropperModel 89 | 90 | //MARK: - 91 | 92 | init(for view: ImageCropperView, with router: ImageCropperRouter, and model: ImageCropperModel) { 93 | self.view = view 94 | self.router = router 95 | self.model = model 96 | } 97 | 98 | } 99 | 100 | //MARK: - ImageCropperPresenter 101 | 102 | extension ImageCropperPresenterImplementation: ImageCropperPresenter { 103 | 104 | func viewDidLoad() { 105 | view?.set(model.image) 106 | } 107 | 108 | func viewDidLayoutSubviews(in frame: CGRect) { 109 | view?.clearMask() 110 | view?.clearBorderAndGrid() 111 | 112 | model.parentFrame = frame 113 | 114 | view?.setImageFrame(model.imageInitialFrame) 115 | view?.drawMask(by: model.mask, with: model.fillColor) 116 | view?.drawBorber(by: model.border, with: model.borderColor) 117 | view?.drawGrid(with: model.grid, with: model.gridColor) 118 | view?.setDone(model.doneTitle) 119 | view?.setCancel(model.cancelTitle) 120 | 121 | view?.setBackButton(title: model.backTitle, image: model.backImage, tintColor: model.backTintColor) 122 | } 123 | 124 | func userInteraction(_ takesPlace: Bool) { 125 | view?.showBottomButtons(!takesPlace) 126 | model.transformatingFinished() 127 | } 128 | 129 | func didDrag(to location: CGPoint) { 130 | view?.setImageFrame(model.draggingFrame(for: location)) 131 | } 132 | 133 | func didPinchStarted() { 134 | model.setStartedPinch() 135 | } 136 | 137 | func didScale(with scale: CGFloat) { 138 | view?.setImageFrame(model.scalingFrame(for: scale)) 139 | } 140 | 141 | func centerImage() { 142 | view?.transformImage(with: model.centerFrame()) 143 | } 144 | 145 | func crop() { 146 | view?.activityIndicator(true) 147 | DispatchQueue.global(qos: .userInitiated).async { 148 | let image = self.model.crop() 149 | // Bounce back to the main thread to update the UI 150 | DispatchQueue.main.async { 151 | self.view?.activityIndicator(false) 152 | self.router.finish(with: image) 153 | } 154 | } 155 | } 156 | 157 | func cancel() { 158 | router.cancel() 159 | } 160 | } 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /ImageCropper/Classes/ImageCropperRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageCropperRouter.swift 3 | // 4 | // Created by NickKopilovskii 5 | // Copyright © NickKopilovskii. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | 10 | class ImageCropperRouterImplementation { 11 | 12 | fileprivate weak var view: ImageCropperViewController? 13 | fileprivate var completionHandler: ImageCropperCompletion 14 | fileprivate var dismiss: ImageCropperDismiss? 15 | 16 | init(for view: ImageCropperViewController, with completionHandler: @escaping ImageCropperCompletion) { 17 | self.view = view 18 | self.completionHandler = completionHandler 19 | } 20 | 21 | 22 | init(for view: ImageCropperViewController, with completionHandler: @escaping ImageCropperCompletion, dismiss: @escaping ImageCropperDismiss) { 23 | self.view = view 24 | self.completionHandler = completionHandler 25 | self.dismiss = dismiss 26 | } 27 | } 28 | 29 | //MARK: - ImageCropperRouter 30 | 31 | extension ImageCropperRouterImplementation: ImageCropperRouter { 32 | func finish(with croppedImage: UIImage) { 33 | self.completionHandler(croppedImage) 34 | if let dismiss = dismiss { dismiss() } 35 | } 36 | 37 | func cancel() { 38 | guard let dismiss = dismiss else { 39 | guard let navigationController = view?.navigationController else { 40 | view?.dismiss(animated: true, completion: nil) 41 | return 42 | } 43 | navigationController.popViewController(animated: true) 44 | return 45 | } 46 | 47 | dismiss() 48 | } 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /ImageCropper/Classes/ImageCropperViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ⚡️Created by Generatus⚡️ on 28.05.2018 3 | // 4 | // ImageCropperViewController.swift 5 | // 6 | // Created by NickKopilovskii 7 | // Copyright © NickKopilovskii. All rights reserved. 8 | // 9 | 10 | import UIKit 11 | 12 | public class ImageCropperViewController: UIViewController { 13 | //MARK: Static initializer 14 | static public func initialize(with configuration:ImageCropperConfiguration, completionHandler: @escaping ImageCropperCompletion) -> ImageCropperViewController { 15 | let cropper = ImageCropperViewController(nibName: "ImageCropper", bundle: Bundle(for: self.classForCoder())) 16 | ImageCropperConfiguratorImplementation.configure(for: cropper, with: configuration, completionHandler: completionHandler) 17 | 18 | return cropper 19 | } 20 | 21 | static public func initialize(with configuration:ImageCropperConfiguration, completionHandler: @escaping ImageCropperCompletion, dismiss: @escaping ImageCropperDismiss) -> ImageCropperViewController { 22 | 23 | let cropper = ImageCropperViewController(nibName: "ImageCropper", bundle: Bundle(for: self.classForCoder())) 24 | ImageCropperConfiguratorImplementation.configure(for: cropper, with: configuration, completionHandler: completionHandler, dismiss: dismiss) 25 | 26 | return cropper 27 | } 28 | 29 | 30 | //MARK: Private properties & IBOutlets 31 | @IBOutlet fileprivate weak var imgCropping: UIImageView! 32 | @IBOutlet fileprivate weak var mask: UIView! 33 | @IBOutlet fileprivate weak var grid: UIView! 34 | @IBOutlet fileprivate weak var btnDone: UIButton! 35 | @IBOutlet fileprivate weak var btnCancel: UIButton! 36 | @IBOutlet fileprivate weak var bottomBar: UIView! 37 | 38 | @IBOutlet fileprivate weak var activityView: UIView! 39 | @IBOutlet fileprivate weak var activity: UIActivityIndicatorView! 40 | 41 | var presenter: ImageCropperPresenter? 42 | 43 | fileprivate var pinchStartDistance: CGFloat = 0 44 | 45 | 46 | //MARK: Lifecicle 47 | override public func viewDidLoad() { 48 | super.viewDidLoad() 49 | presenter?.viewDidLoad() 50 | } 51 | 52 | override public func viewDidLayoutSubviews() { 53 | super.viewDidLayoutSubviews() 54 | presenter?.viewDidLayoutSubviews(in: view.bounds) 55 | } 56 | } 57 | 58 | //MARK: - Private 59 | //MARK: Actions 60 | extension ImageCropperViewController { 61 | @IBAction func btnCancelPressed(_ sender: UIButton) { 62 | presenter?.cancel() 63 | } 64 | 65 | @IBAction func btnDonePressed(_ sender: UIButton) { 66 | presenter?.crop() 67 | } 68 | 69 | @IBAction func actionPan(_ sender: UIPanGestureRecognizer) { 70 | switch sender.state { 71 | case .began: 72 | presenter?.userInteraction(true) 73 | 74 | case .changed: 75 | presenter?.didDrag(to: sender.location(in: grid)) 76 | 77 | case .ended: 78 | presenter?.userInteraction(false) 79 | 80 | default: 81 | return 82 | } 83 | } 84 | 85 | @IBAction func actionPinch(_ sender: UIPinchGestureRecognizer) { 86 | switch sender.state { 87 | case .began: 88 | guard sender.numberOfTouches >= 2 else { return } 89 | presenter?.userInteraction(true) 90 | presenter?.didPinchStarted() 91 | case .changed: 92 | guard sender.numberOfTouches >= 2 else { return } 93 | presenter?.didScale(with: sender.scale) 94 | case .ended, .cancelled: 95 | presenter?.userInteraction(false) 96 | default: 97 | return 98 | } 99 | } 100 | 101 | @IBAction func actionGesture(_ sender: UITapGestureRecognizer) { 102 | 103 | } 104 | 105 | @IBAction func actionDoubleTap(_ sender: UITapGestureRecognizer) { 106 | presenter?.centerImage() 107 | } 108 | 109 | func distance(from first: CGPoint, to second: CGPoint) -> CGFloat { 110 | 111 | return sqrt(pow(first.x - second.x, 2) + pow(first.y - second.y, 2)) 112 | } 113 | } 114 | 115 | //MARK: - ImageCropperView 116 | 117 | extension ImageCropperViewController: ImageCropperView { 118 | 119 | func set(_ image: UIImage) { 120 | imgCropping.image = image 121 | } 122 | 123 | func setImageFrame(_ frame: CGRect) { 124 | imgCropping.frame = frame 125 | } 126 | 127 | func transformImage(with frame: CGRect) { 128 | UIView.animate(withDuration: 0.2) { 129 | self.imgCropping.frame = frame 130 | } 131 | } 132 | 133 | func clearMask() { 134 | mask.layer.mask = nil 135 | mask.layer.sublayers?.forEach({ (sublayer) in 136 | sublayer.removeFromSuperlayer() 137 | }) 138 | } 139 | 140 | func drawMask(by path: CGPath, with fillColor: UIColor) { 141 | let hole = CAShapeLayer() 142 | hole.frame = mask.bounds 143 | hole.path = path 144 | hole.fillRule = CAShapeLayerFillRule.evenOdd 145 | mask.layer.mask = hole 146 | mask.backgroundColor = fillColor 147 | } 148 | 149 | func clearBorderAndGrid() { 150 | grid.layer.sublayers?.forEach({ $0.removeFromSuperlayer() }) 151 | } 152 | 153 | func drawBorber(by path: CGPath, with strokeColor: CGColor) { 154 | let border = CAShapeLayer() 155 | border.frame = grid.bounds 156 | border.path = path 157 | border.fillColor = UIColor.clear.cgColor 158 | border.strokeColor = strokeColor 159 | border.lineWidth = 4 160 | grid.layer.addSublayer(border) 161 | } 162 | 163 | func drawGrid(with lines: [CGPath], with strokeColor: CGColor) { 164 | lines.forEach { 165 | let lineLayer = CAShapeLayer() 166 | lineLayer.path = $0 167 | lineLayer.fillColor = nil 168 | lineLayer.opacity = 1 169 | lineLayer.lineWidth = 1 170 | lineLayer.strokeColor = strokeColor 171 | grid.layer.insertSublayer(lineLayer, at: 0) 172 | } 173 | } 174 | 175 | 176 | func setDone(_ title: String?) { 177 | guard let t = title else { return } 178 | btnDone.setTitle(t, for: .normal) 179 | } 180 | 181 | func setCancel(_ title: String?) { 182 | guard let t = title else { return } 183 | btnCancel.setTitle(t, for: .normal) 184 | } 185 | 186 | func showBottomButtons(_ show: Bool) { 187 | let alpha = show ? 1 : 0 188 | UIView.animate(withDuration: 0.1) { 189 | self.bottomBar.alpha = CGFloat(alpha) 190 | } 191 | } 192 | 193 | 194 | func setBackButton(title: String?, image: UIImage?, tintColor: UIColor?) { 195 | guard let bar = self.navigationController?.navigationBar, let backItem = bar.backItem else { return } 196 | backItem.title = title 197 | bar.backIndicatorImage = image 198 | bar.backIndicatorTransitionMaskImage = image 199 | bar.tintColor = tintColor ?? bar.tintColor 200 | } 201 | 202 | 203 | func activityIndicator(_ show: Bool) { 204 | activityView.isHidden = !show 205 | } 206 | 207 | // func setBack(title: String?) { 208 | // guard let back = navigationItem.backBarButtonItem else { return } 209 | // guard let backTitle = title else { return } 210 | // back.title = backTitle 211 | // 212 | // } 213 | // 214 | // func setBack(image: UIImage?) { 215 | // guard let nc = navigationController, let back = nc.navigationBar.backItem?.backBarButtonItem else { return } 216 | // guard let backImage = image else { return } 217 | // back.image = backImage 218 | // } 219 | // 220 | // func setBack(tintColor: UIColor?) { 221 | // guard let nc = navigationController, let back = nc.navigationBar.backItem?.backBarButtonItem else { return } 222 | // guard let backTintColor = tintColor else { return } 223 | // back.tintColor = backTintColor 224 | // } 225 | 226 | } 227 | 228 | -------------------------------------------------------------------------------- /ImageCropper/Classes/UIImage+NormalizeOrientation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+NormalizeOrientation.swift 3 | // ImageCropper 4 | // 5 | // Created by Nick Kopilovskii on 26.07.2018. 6 | // 7 | 8 | import UIKit 9 | 10 | public extension UIImage { 11 | func normalizeOrientation() -> UIImage { 12 | guard imageOrientation != .up else { return self } 13 | UIGraphicsBeginImageContext(size) 14 | draw(in: CGRect(origin: .zero, size: size)) 15 | let normalizedImage = UIGraphicsGetImageFromCurrentImageContext() 16 | UIGraphicsEndImageContext() 17 | return normalizedImage ?? self 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Nick Kopilovskii 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ImageCropper 2 | 3 | ![Swift](https://img.shields.io/badge/Swift-4.0-orange.svg) 4 | [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](http://mit-license.org) 5 | [![Platform](http://img.shields.io/badge/platform-ios-lightgrey.svg?style=flat)](https://developer.apple.com/resources/) 6 | 7 | 8 | ## Info 9 | Basis on [MVP + Clean Architecture] (https://github.com/FortechRomania/ios-mvp-clean-architecture/) 10 | 11 | Created with [Generatus] (https://github.com/Ryasnoy/Generatus) 12 | 13 | ## Description 14 | Module for implementing the process of cropping images 15 | 16 | In the process of creating a variety of projects, developers often face the need to crop images (whether the user's avatar on the social network, background images, and so on). 17 | 18 | Of course, iOS provides its own tools for image processing using the `Photos` application, but its use is not always convenient, justified, or even possible. 19 | 20 | This library provides the ability to cut out sections of the original image in specified proportions by user's gesture interactions. 21 | 22 | 23 | ## Interface 24 | 25 | ### ImageCropperCompletion 26 | `public typealias ImageCropperCompletion = (UIImage?) -> Void` - closure which is performed upon completion of the image cropping 27 | 28 | ### ImageCropperConfiguration 29 | `ImageCropperFigureType` - figure types for cropping: 30 | - `circle` - circle 31 | 32 | - `square` - square (rectangle with aspect ratio 1 to 1) 33 | 34 | - `rect2x1` - square (rectangle with aspect ratio* 2 to 1) 35 | 36 | - `rect1x2` - square (rectangle with aspect ratio* 1 to 2) 37 | 38 | - `rect4x3` - square (rectangle with aspect ratio* 4 to 3) 39 | 40 | - `rect3x4` - square (rectangle with aspect ratio* 3 to 4) 41 | 42 | - `rect16x9` - square (rectangle with aspect ratio* 16 to 9) 43 | 44 | - `rect9x16` - square (rectangle with aspect ratio* 9 to 16) 45 | 46 | - `customRect` - square (rectangle with custom aspect ratio) 47 | 48 | *(first number is width, second - height) 49 | 50 | #### Сustom parameters 51 | `customRatio` - size for creating figure with custom aspect ratio. Value of this property will be used only if `figure = .customRect`. Default value - `CGSize(width: 1, height: 1)` 52 | 53 | `maskFillColor` - fill color around cropped figure ("hole") 54 | 55 | `borderColor` - color of cropped figure's ("hole") border 56 | 57 | `showGrid` - specifies whether to display the grid 58 | 59 | `gridColor` - color of grid's lines 60 | 61 | `doneTitle` - title text of button for finishing cropping process (default: `Done`) 62 | 63 | `cancelTitle` - itle text of button for canceling cropping process (default: `Cancel`) 64 | 65 | ## Updates 66 | 67 | ### v.0.1.4 68 | 69 | **Added:** 70 | 71 | - variable corner radius for cutted figure 72 | 73 | **Fixed:** 74 | 75 | - pinch gesture issue (thanks [davidpaul0880](https://github.com/davidpaul0880) for help) 76 | 77 | 78 | ### v.0.1.5 79 | **Added:** 80 | 81 | - Swift 5 support 82 | 83 | ## Usage Example 84 | 85 | ### Module Initialization 86 | Set configuration: 87 | ``` 88 | var config = ImageCropperConfiguration(with: img, and: figure) 89 | config.maskFillColor = UIColor(displayP3Red: 0.7, green: 0.5, blue: 0.2, alpha: 0.75) 90 | config.borderColor = UIColor.black 91 | 92 | config.showGrid = true 93 | config.gridColor = UIColor.white 94 | config.doneTitle = "CROP" 95 | config.cancelTitle = "Back" 96 | ``` 97 | 98 | Initialize view controller: 99 | ``` 100 | let cropper = ImageCropperViewController.initialize(with: config) { croppedImage in 101 | /* 102 | Code to perform after finishing cropping process 103 | */ 104 | } 105 | ``` 106 | or 107 | ``` 108 | let cropper = ImageCropperViewController.initialize(with: config, completionHandler: { _croppedImage in 109 | /* 110 | Code to perform after finishing cropping process 111 | */ 112 | }) { 113 | /* 114 | Code to perform after dismissing controller 115 | */ 116 | } 117 | ``` 118 | 119 | Display with Navigation Controller: 120 | ``` 121 | navigationController.pushViewController(cropper, animated: true) 122 | ``` 123 | 124 | Present Modally: 125 | ``` 126 | viewController.present(cropper, animated: true, completion: nil) 127 | ``` 128 | 129 | ### User interaction 130 | `UIPanGestureRecognizer` - gesture for draging image below mask and grid 131 | 132 | `UIPinchGestureRecognizer` - gesture for scaling image 133 | 134 | `UITapGestureRecognizer` - double tap for centering and transforming image to the initial frame 135 | 136 | ### Suported Screen Orientation 137 | iPhone Portrait 138 | 139 | ![](https://github.com/nkopilovskii/ImageCropper/blob/master/Example/ImageCropper/iPhone_Portrait.png) 140 | 141 | iPhone Landscape 142 | 143 | ![](https://github.com/nkopilovskii/ImageCropper/blob/master/Example/ImageCropper/iPhone_Landscape.png) 144 | 145 | iPad Portrait 146 | 147 | ![](https://github.com/nkopilovskii/ImageCropper/blob/master/Example/ImageCropper/iPad_Portrait.png) 148 | 149 | iPad Landscape 150 | 151 | ![](https://github.com/nkopilovskii/ImageCropper/blob/master/Example/ImageCropper/iPhone_Landscape.png) 152 | 153 | ## Requirements 154 | - iOS 11.0+ 155 | - Xcode 9.0 156 | 157 | ## Installation 158 | 159 | ImageCropper is available through [CocoaPods](https://cocoapods.org). To install 160 | it, simply add the following line to your Podfile: 161 | 162 | ``` 163 | pod 'ImageCropper' 164 | ``` 165 | 166 | ## Author 167 | 168 | Nick Kopilovskii, nkopilovskii@gmail.com 169 | 170 | ## License 171 | 172 | ImageCropper is available under the MIT license. See the LICENSE file for more info. 173 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj --------------------------------------------------------------------------------