├── .swift-version ├── Demo.gif ├── ImageFreeCut.podspec ├── ImageFreeCut.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── Cem.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── ImageFreeCut.xcscheme │ └── xcschememanagement.plist ├── ImageFreeCut ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── ImageFreeCutView.swift ├── Info.plist └── ViewController.swift ├── LICENSE └── Readme.md /.swift-version: -------------------------------------------------------------------------------- 1 | 3.0 2 | -------------------------------------------------------------------------------- /Demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cemolcay/ImageFreeCut/cdddff205d6e59d943ddbc30595045994f98b808/Demo.gif -------------------------------------------------------------------------------- /ImageFreeCut.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint ImageFreeCut.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 12 | # 13 | # These will help people to find your library, and whilst it 14 | # can feel like a chore to fill in it's definitely to your advantage. The 15 | # summary should be tweet-length, and the description more in depth. 16 | # 17 | 18 | s.name = "ImageFreeCut" 19 | s.version = "0.0.1" 20 | s.summary = "A UIView subclass lets you draw a path over a image and crops thats part." 21 | 22 | # This description is used to generate tags and improve search results. 23 | # * Think: What does it do? Why did you write it? What is the focus? 24 | # * Try to keep it short, snappy and to the point. 25 | # * Write the description between the DESC delimiters below. 26 | # * Finally, don't worry about the indent, CocoaPods strips it! 27 | s.description = <<-DESC 28 | ImageFreeCut 29 | === 30 | 31 | A UIView subclass lets you draw a path over a image and crops thats part. 32 | 33 | Demo 34 | ---- 35 | 36 | ![alt tag]() 37 | 38 | Install 39 | ---- 40 | 41 | ``` ruby 42 | use_frameworks! 43 | pod 'ImageFreeCut' 44 | ``` 45 | 46 | Usage 47 | ---- 48 | 49 | ``` swift 50 | import ImageFreeCut 51 | ``` 52 | 53 | You can either use storyboard or create it programmatically like any UIView. 54 | Notice that view's class name is `ImageFreeCutView`. 55 | 56 | Sets the image of `ImageFreeCutView`. You are going to crop that one. 57 | ``` 58 | open var imageToCut: UIImage? 59 | ``` 60 | 61 | You can set the imageView's properties like `contentMode` directly from your `ImageFreeCutView` instance. 62 | ``` 63 | open var imageView: UIImageView! 64 | ``` 65 | 66 | You can set cut layer's properties like `strokeColor`, `fillColor`, `dashPattern` directly from your `ImageFreeCutView` instance. 67 | ``` 68 | open var imageCutShapeLayer: CAShapeLayer! 69 | ``` 70 | 71 | ImageFreeCutViewDelegate 72 | ---- 73 | Set your instance delegate and implement 74 | 75 | ``` 76 | func imageFreeCutView(_ imageFreeCutView: ImageFreeCutView, didCut image: UIImage?) 77 | ``` 78 | 79 | delegate method to retrive cropped image. 80 | 81 | DESC 82 | 83 | s.homepage = "https://github.com/cemolcay/ImageFreeCut" 84 | # s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" 85 | 86 | 87 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 88 | # 89 | # Licensing your code is important. See http://choosealicense.com for more info. 90 | # CocoaPods will detect a license file if there is a named LICENSE* 91 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 92 | # 93 | 94 | s.license = "MIT" 95 | # s.license = { :type => "MIT", :file => "FILE_LICENSE" } 96 | 97 | 98 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 99 | # 100 | # Specify the authors of the library, with email addresses. Email addresses 101 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 102 | # accepts just a name if you'd rather not provide an email address. 103 | # 104 | # Specify a social_media_url where others can refer to, for example a twitter 105 | # profile URL. 106 | # 107 | 108 | s.author = { "cemolcay" => "ccemolcay@gmail.com" } 109 | # Or just: s.author = "cemolcay" 110 | # s.authors = { "cemolcay" => "ccemolcay@gmail.com" } 111 | # s.social_media_url = "http://twitter.com/cemolcay" 112 | 113 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 114 | # 115 | # If this Pod runs only on iOS or OS X, then specify the platform and 116 | # the deployment target. You can optionally include the target after the platform. 117 | # 118 | 119 | # s.platform = :ios 120 | s.platform = :ios, "10.0" 121 | 122 | # When using multiple platforms 123 | # s.ios.deployment_target = "5.0" 124 | # s.osx.deployment_target = "10.7" 125 | # s.watchos.deployment_target = "2.0" 126 | # s.tvos.deployment_target = "9.0" 127 | 128 | 129 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 130 | # 131 | # Specify the location from where the source should be retrieved. 132 | # Supports git, hg, bzr, svn and HTTP. 133 | # 134 | 135 | s.source = { :git => "https://github.com/cemolcay/ImageFreeCut.git", :tag => "#{s.version}" } 136 | 137 | 138 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 139 | # 140 | # CocoaPods is smart about how it includes source code. For source files 141 | # giving a folder will include any swift, h, m, mm, c & cpp files. 142 | # For header files it will include any header in the folder. 143 | # Not including the public_header_files will make all headers public. 144 | # 145 | 146 | s.source_files = "ImageFreeCut/ImageFreeCutView.swift" 147 | # s.exclude_files = "Classes/Exclude" 148 | 149 | # s.public_header_files = "Classes/**/*.h" 150 | 151 | 152 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 153 | # 154 | # A list of resources included with the Pod. These are copied into the 155 | # target bundle with a build phase script. Anything else will be cleaned. 156 | # You can preserve files from being cleaned, please don't preserve 157 | # non-essential files like tests, examples and documentation. 158 | # 159 | 160 | # s.resource = "icon.png" 161 | # s.resources = "Resources/*.png" 162 | 163 | # s.preserve_paths = "FilesToSave", "MoreFilesToSave" 164 | 165 | 166 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 167 | # 168 | # Link your library with frameworks, or libraries. Libraries do not include 169 | # the lib prefix of their name. 170 | # 171 | 172 | # s.framework = "SomeFramework" 173 | # s.frameworks = "SomeFramework", "AnotherFramework" 174 | 175 | # s.library = "iconv" 176 | # s.libraries = "iconv", "xml2" 177 | 178 | 179 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 180 | # 181 | # If your library depends on compiler flags you can set them in the xcconfig hash 182 | # where they will only apply to your library. If you depend on other Podspecs 183 | # you can include multiple dependencies to ensure it works. 184 | 185 | s.requires_arc = true 186 | 187 | # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 188 | # s.dependency "JSONKit", "~> 1.4" 189 | 190 | end 191 | -------------------------------------------------------------------------------- /ImageFreeCut.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B228A3141DB4D7EE00411771 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B228A3131DB4D7EE00411771 /* AppDelegate.swift */; }; 11 | B228A3161DB4D7EE00411771 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B228A3151DB4D7EE00411771 /* ViewController.swift */; }; 12 | B228A3191DB4D7EE00411771 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B228A3171DB4D7EE00411771 /* Main.storyboard */; }; 13 | B228A31B1DB4D7EE00411771 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B228A31A1DB4D7EE00411771 /* Assets.xcassets */; }; 14 | B228A31E1DB4D7EE00411771 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B228A31C1DB4D7EE00411771 /* LaunchScreen.storyboard */; }; 15 | B228A3281DB4F9CA00411771 /* ImageFreeCutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B228A3271DB4F9CA00411771 /* ImageFreeCutView.swift */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXFileReference section */ 19 | B228A3101DB4D7EE00411771 /* ImageFreeCut.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ImageFreeCut.app; sourceTree = BUILT_PRODUCTS_DIR; }; 20 | B228A3131DB4D7EE00411771 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 21 | B228A3151DB4D7EE00411771 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 22 | B228A3181DB4D7EE00411771 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 23 | B228A31A1DB4D7EE00411771 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 24 | B228A31D1DB4D7EE00411771 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 25 | B228A31F1DB4D7EE00411771 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 26 | B228A3271DB4F9CA00411771 /* ImageFreeCutView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageFreeCutView.swift; sourceTree = ""; }; 27 | /* End PBXFileReference section */ 28 | 29 | /* Begin PBXFrameworksBuildPhase section */ 30 | B228A30D1DB4D7EE00411771 /* Frameworks */ = { 31 | isa = PBXFrameworksBuildPhase; 32 | buildActionMask = 2147483647; 33 | files = ( 34 | ); 35 | runOnlyForDeploymentPostprocessing = 0; 36 | }; 37 | /* End PBXFrameworksBuildPhase section */ 38 | 39 | /* Begin PBXGroup section */ 40 | B228A3071DB4D7EE00411771 = { 41 | isa = PBXGroup; 42 | children = ( 43 | B228A3121DB4D7EE00411771 /* ImageFreeCut */, 44 | B228A3111DB4D7EE00411771 /* Products */, 45 | ); 46 | sourceTree = ""; 47 | }; 48 | B228A3111DB4D7EE00411771 /* Products */ = { 49 | isa = PBXGroup; 50 | children = ( 51 | B228A3101DB4D7EE00411771 /* ImageFreeCut.app */, 52 | ); 53 | name = Products; 54 | sourceTree = ""; 55 | }; 56 | B228A3121DB4D7EE00411771 /* ImageFreeCut */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | B228A3131DB4D7EE00411771 /* AppDelegate.swift */, 60 | B228A3151DB4D7EE00411771 /* ViewController.swift */, 61 | B228A3271DB4F9CA00411771 /* ImageFreeCutView.swift */, 62 | B228A3171DB4D7EE00411771 /* Main.storyboard */, 63 | B228A31A1DB4D7EE00411771 /* Assets.xcassets */, 64 | B228A31C1DB4D7EE00411771 /* LaunchScreen.storyboard */, 65 | B228A31F1DB4D7EE00411771 /* Info.plist */, 66 | ); 67 | path = ImageFreeCut; 68 | sourceTree = ""; 69 | }; 70 | /* End PBXGroup section */ 71 | 72 | /* Begin PBXNativeTarget section */ 73 | B228A30F1DB4D7EE00411771 /* ImageFreeCut */ = { 74 | isa = PBXNativeTarget; 75 | buildConfigurationList = B228A3221DB4D7EE00411771 /* Build configuration list for PBXNativeTarget "ImageFreeCut" */; 76 | buildPhases = ( 77 | B228A30C1DB4D7EE00411771 /* Sources */, 78 | B228A30D1DB4D7EE00411771 /* Frameworks */, 79 | B228A30E1DB4D7EE00411771 /* Resources */, 80 | ); 81 | buildRules = ( 82 | ); 83 | dependencies = ( 84 | ); 85 | name = ImageFreeCut; 86 | productName = ImageFreeCut; 87 | productReference = B228A3101DB4D7EE00411771 /* ImageFreeCut.app */; 88 | productType = "com.apple.product-type.application"; 89 | }; 90 | /* End PBXNativeTarget section */ 91 | 92 | /* Begin PBXProject section */ 93 | B228A3081DB4D7EE00411771 /* Project object */ = { 94 | isa = PBXProject; 95 | attributes = { 96 | LastSwiftUpdateCheck = 0800; 97 | LastUpgradeCheck = 0800; 98 | ORGANIZATIONNAME = Mojilala; 99 | TargetAttributes = { 100 | B228A30F1DB4D7EE00411771 = { 101 | CreatedOnToolsVersion = 8.0; 102 | DevelopmentTeam = VYSS4GN9S2; 103 | ProvisioningStyle = Automatic; 104 | }; 105 | }; 106 | }; 107 | buildConfigurationList = B228A30B1DB4D7EE00411771 /* Build configuration list for PBXProject "ImageFreeCut" */; 108 | compatibilityVersion = "Xcode 3.2"; 109 | developmentRegion = English; 110 | hasScannedForEncodings = 0; 111 | knownRegions = ( 112 | en, 113 | Base, 114 | ); 115 | mainGroup = B228A3071DB4D7EE00411771; 116 | productRefGroup = B228A3111DB4D7EE00411771 /* Products */; 117 | projectDirPath = ""; 118 | projectRoot = ""; 119 | targets = ( 120 | B228A30F1DB4D7EE00411771 /* ImageFreeCut */, 121 | ); 122 | }; 123 | /* End PBXProject section */ 124 | 125 | /* Begin PBXResourcesBuildPhase section */ 126 | B228A30E1DB4D7EE00411771 /* Resources */ = { 127 | isa = PBXResourcesBuildPhase; 128 | buildActionMask = 2147483647; 129 | files = ( 130 | B228A31E1DB4D7EE00411771 /* LaunchScreen.storyboard in Resources */, 131 | B228A31B1DB4D7EE00411771 /* Assets.xcassets in Resources */, 132 | B228A3191DB4D7EE00411771 /* Main.storyboard in Resources */, 133 | ); 134 | runOnlyForDeploymentPostprocessing = 0; 135 | }; 136 | /* End PBXResourcesBuildPhase section */ 137 | 138 | /* Begin PBXSourcesBuildPhase section */ 139 | B228A30C1DB4D7EE00411771 /* Sources */ = { 140 | isa = PBXSourcesBuildPhase; 141 | buildActionMask = 2147483647; 142 | files = ( 143 | B228A3281DB4F9CA00411771 /* ImageFreeCutView.swift in Sources */, 144 | B228A3161DB4D7EE00411771 /* ViewController.swift in Sources */, 145 | B228A3141DB4D7EE00411771 /* AppDelegate.swift in Sources */, 146 | ); 147 | runOnlyForDeploymentPostprocessing = 0; 148 | }; 149 | /* End PBXSourcesBuildPhase section */ 150 | 151 | /* Begin PBXVariantGroup section */ 152 | B228A3171DB4D7EE00411771 /* Main.storyboard */ = { 153 | isa = PBXVariantGroup; 154 | children = ( 155 | B228A3181DB4D7EE00411771 /* Base */, 156 | ); 157 | name = Main.storyboard; 158 | sourceTree = ""; 159 | }; 160 | B228A31C1DB4D7EE00411771 /* LaunchScreen.storyboard */ = { 161 | isa = PBXVariantGroup; 162 | children = ( 163 | B228A31D1DB4D7EE00411771 /* Base */, 164 | ); 165 | name = LaunchScreen.storyboard; 166 | sourceTree = ""; 167 | }; 168 | /* End PBXVariantGroup section */ 169 | 170 | /* Begin XCBuildConfiguration section */ 171 | B228A3201DB4D7EE00411771 /* Debug */ = { 172 | isa = XCBuildConfiguration; 173 | buildSettings = { 174 | ALWAYS_SEARCH_USER_PATHS = NO; 175 | CLANG_ANALYZER_NONNULL = YES; 176 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 177 | CLANG_CXX_LIBRARY = "libc++"; 178 | CLANG_ENABLE_MODULES = YES; 179 | CLANG_ENABLE_OBJC_ARC = YES; 180 | CLANG_WARN_BOOL_CONVERSION = YES; 181 | CLANG_WARN_CONSTANT_CONVERSION = YES; 182 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 183 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 184 | CLANG_WARN_EMPTY_BODY = YES; 185 | CLANG_WARN_ENUM_CONVERSION = YES; 186 | CLANG_WARN_INFINITE_RECURSION = YES; 187 | CLANG_WARN_INT_CONVERSION = YES; 188 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 189 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 190 | CLANG_WARN_UNREACHABLE_CODE = YES; 191 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 192 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 193 | COPY_PHASE_STRIP = NO; 194 | DEBUG_INFORMATION_FORMAT = dwarf; 195 | ENABLE_STRICT_OBJC_MSGSEND = YES; 196 | ENABLE_TESTABILITY = YES; 197 | GCC_C_LANGUAGE_STANDARD = gnu99; 198 | GCC_DYNAMIC_NO_PIC = NO; 199 | GCC_NO_COMMON_BLOCKS = YES; 200 | GCC_OPTIMIZATION_LEVEL = 0; 201 | GCC_PREPROCESSOR_DEFINITIONS = ( 202 | "DEBUG=1", 203 | "$(inherited)", 204 | ); 205 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 206 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 207 | GCC_WARN_UNDECLARED_SELECTOR = YES; 208 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 209 | GCC_WARN_UNUSED_FUNCTION = YES; 210 | GCC_WARN_UNUSED_VARIABLE = YES; 211 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 212 | MTL_ENABLE_DEBUG_INFO = YES; 213 | ONLY_ACTIVE_ARCH = YES; 214 | SDKROOT = iphoneos; 215 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 216 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 217 | TARGETED_DEVICE_FAMILY = "1,2"; 218 | }; 219 | name = Debug; 220 | }; 221 | B228A3211DB4D7EE00411771 /* Release */ = { 222 | isa = XCBuildConfiguration; 223 | buildSettings = { 224 | ALWAYS_SEARCH_USER_PATHS = NO; 225 | CLANG_ANALYZER_NONNULL = YES; 226 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 227 | CLANG_CXX_LIBRARY = "libc++"; 228 | CLANG_ENABLE_MODULES = YES; 229 | CLANG_ENABLE_OBJC_ARC = YES; 230 | CLANG_WARN_BOOL_CONVERSION = YES; 231 | CLANG_WARN_CONSTANT_CONVERSION = YES; 232 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 233 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 234 | CLANG_WARN_EMPTY_BODY = YES; 235 | CLANG_WARN_ENUM_CONVERSION = YES; 236 | CLANG_WARN_INFINITE_RECURSION = YES; 237 | CLANG_WARN_INT_CONVERSION = YES; 238 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 239 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 240 | CLANG_WARN_UNREACHABLE_CODE = YES; 241 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 242 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 243 | COPY_PHASE_STRIP = NO; 244 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 245 | ENABLE_NS_ASSERTIONS = NO; 246 | ENABLE_STRICT_OBJC_MSGSEND = YES; 247 | GCC_C_LANGUAGE_STANDARD = gnu99; 248 | GCC_NO_COMMON_BLOCKS = YES; 249 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 250 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 251 | GCC_WARN_UNDECLARED_SELECTOR = YES; 252 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 253 | GCC_WARN_UNUSED_FUNCTION = YES; 254 | GCC_WARN_UNUSED_VARIABLE = YES; 255 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 256 | MTL_ENABLE_DEBUG_INFO = NO; 257 | SDKROOT = iphoneos; 258 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 259 | TARGETED_DEVICE_FAMILY = "1,2"; 260 | VALIDATE_PRODUCT = YES; 261 | }; 262 | name = Release; 263 | }; 264 | B228A3231DB4D7EE00411771 /* Debug */ = { 265 | isa = XCBuildConfiguration; 266 | buildSettings = { 267 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 268 | DEVELOPMENT_TEAM = VYSS4GN9S2; 269 | INFOPLIST_FILE = ImageFreeCut/Info.plist; 270 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 271 | PRODUCT_BUNDLE_IDENTIFIER = com.mojilala.ImageFreeCut; 272 | PRODUCT_NAME = "$(TARGET_NAME)"; 273 | SWIFT_VERSION = 3.0; 274 | }; 275 | name = Debug; 276 | }; 277 | B228A3241DB4D7EE00411771 /* Release */ = { 278 | isa = XCBuildConfiguration; 279 | buildSettings = { 280 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 281 | DEVELOPMENT_TEAM = VYSS4GN9S2; 282 | INFOPLIST_FILE = ImageFreeCut/Info.plist; 283 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 284 | PRODUCT_BUNDLE_IDENTIFIER = com.mojilala.ImageFreeCut; 285 | PRODUCT_NAME = "$(TARGET_NAME)"; 286 | SWIFT_VERSION = 3.0; 287 | }; 288 | name = Release; 289 | }; 290 | /* End XCBuildConfiguration section */ 291 | 292 | /* Begin XCConfigurationList section */ 293 | B228A30B1DB4D7EE00411771 /* Build configuration list for PBXProject "ImageFreeCut" */ = { 294 | isa = XCConfigurationList; 295 | buildConfigurations = ( 296 | B228A3201DB4D7EE00411771 /* Debug */, 297 | B228A3211DB4D7EE00411771 /* Release */, 298 | ); 299 | defaultConfigurationIsVisible = 0; 300 | defaultConfigurationName = Release; 301 | }; 302 | B228A3221DB4D7EE00411771 /* Build configuration list for PBXNativeTarget "ImageFreeCut" */ = { 303 | isa = XCConfigurationList; 304 | buildConfigurations = ( 305 | B228A3231DB4D7EE00411771 /* Debug */, 306 | B228A3241DB4D7EE00411771 /* Release */, 307 | ); 308 | defaultConfigurationIsVisible = 0; 309 | }; 310 | /* End XCConfigurationList section */ 311 | }; 312 | rootObject = B228A3081DB4D7EE00411771 /* Project object */; 313 | } 314 | -------------------------------------------------------------------------------- /ImageFreeCut.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ImageFreeCut.xcodeproj/xcuserdata/Cem.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /ImageFreeCut.xcodeproj/xcuserdata/Cem.xcuserdatad/xcschemes/ImageFreeCut.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /ImageFreeCut.xcodeproj/xcuserdata/Cem.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ImageFreeCut.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | B228A30F1DB4D7EE00411771 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /ImageFreeCut/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ImageFreeCut 4 | // 5 | // Created by Cem Olcay on 17/10/16. 6 | // Copyright © 2016 Mojilala. 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 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /ImageFreeCut/Assets.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" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /ImageFreeCut/Base.lproj/LaunchScreen.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 | -------------------------------------------------------------------------------- /ImageFreeCut/Base.lproj/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 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 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 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /ImageFreeCut/ImageFreeCutView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageFreeCutView.swift 3 | // ImageFreeCut 4 | // 5 | // Created by Cem Olcay on 17/10/16. 6 | // Copyright © 2016 Mojilala. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import QuartzCore 11 | 12 | public protocol ImageFreeCutViewDelegate: class { 13 | func imageFreeCutView(_ imageFreeCutView: ImageFreeCutView, didCut image: UIImage?) 14 | } 15 | 16 | open class ImageFreeCutView: UIView { 17 | open var imageView: UIImageView! 18 | open var imageCutShapeLayer: CAShapeLayer! 19 | 20 | open weak var delegate: ImageFreeCutViewDelegate? 21 | open var imageToCut: UIImage? { 22 | didSet { 23 | imageView.image = imageToCut 24 | } 25 | } 26 | 27 | private var drawPoints: [CGPoint] = [] { 28 | didSet { 29 | drawShape() 30 | } 31 | } 32 | 33 | // MARK: Init 34 | override public init(frame: CGRect) { 35 | super.init(frame: frame) 36 | commonInit() 37 | } 38 | 39 | required public init?(coder aDecoder: NSCoder) { 40 | super.init(coder: aDecoder) 41 | commonInit() 42 | } 43 | 44 | private func commonInit() { 45 | // Setup image view 46 | imageView = UIImageView(frame: frame) 47 | addSubview(imageView) 48 | imageView.image = imageToCut 49 | imageView.isUserInteractionEnabled = false 50 | imageView.translatesAutoresizingMaskIntoConstraints = false 51 | imageView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true 52 | imageView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true 53 | imageView.topAnchor.constraint(equalTo: topAnchor).isActive = true 54 | imageView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true 55 | 56 | // Setup image cut shape layer 57 | imageCutShapeLayer = CAShapeLayer() 58 | imageCutShapeLayer.frame = imageView.bounds 59 | imageCutShapeLayer.fillColor = UIColor.clear.cgColor 60 | imageCutShapeLayer.lineWidth = 1 61 | imageCutShapeLayer.strokeColor = UIColor.black.cgColor 62 | imageCutShapeLayer.lineJoin = kCALineJoinRound 63 | imageCutShapeLayer.lineDashPattern = [4, 4] 64 | imageView.layer.addSublayer(imageCutShapeLayer) 65 | } 66 | 67 | // MARK: Lifecycle 68 | override open func layoutSubviews() { 69 | super.layoutSubviews() 70 | imageCutShapeLayer.frame = imageView.bounds 71 | } 72 | 73 | // MARK: Touch Handling 74 | override open func touchesBegan(_ touches: Set, with event: UIEvent?) { 75 | super.touchesBegan(touches, with: event) 76 | guard let touchPosition = touches.first?.location(in: imageView) else { return } 77 | drawPoints.append(touchPosition) 78 | } 79 | 80 | override open func touchesMoved(_ touches: Set, with event: UIEvent?) { 81 | super.touchesMoved(touches, with: event) 82 | guard let touchPosition = touches.first?.location(in: imageView) else { return } 83 | drawPoints.append(touchPosition) 84 | } 85 | 86 | override open func touchesEnded(_ touches: Set, with event: UIEvent?) { 87 | super.touchesEnded(touches, with: event) 88 | guard let touchPosition = touches.first?.location(in: imageView) else { return } 89 | drawPoints.append(touchPosition) 90 | 91 | // Close path 92 | guard let cgPath = imageCutShapeLayer.path else { return } 93 | let path = UIBezierPath(cgPath: cgPath) 94 | path.close() 95 | imageCutShapeLayer.path = path.cgPath 96 | 97 | // Notify delegate 98 | delegate?.imageFreeCutView(self, didCut: cropImage()) 99 | resetShape() 100 | } 101 | 102 | // MARK: Cutting Crew 103 | private func resetShape() { 104 | drawPoints = [] 105 | imageView.layer.mask = nil 106 | } 107 | 108 | private func drawShape() { 109 | if drawPoints.isEmpty { 110 | imageCutShapeLayer.path = nil 111 | return 112 | } 113 | 114 | let path = UIBezierPath() 115 | for (index, point) in drawPoints.enumerated() { 116 | if index == 0 { 117 | path.move(to: point) 118 | } else { 119 | path.addLine(to: point) 120 | } 121 | } 122 | 123 | imageCutShapeLayer.path = path.cgPath 124 | } 125 | 126 | private func cropImage() -> UIImage? { 127 | guard let originalImage = imageToCut, let cgPath = imageCutShapeLayer.path else { return nil } 128 | 129 | let path = UIBezierPath(cgPath: cgPath) 130 | UIGraphicsBeginImageContextWithOptions(imageView.frame.size, false, 0) 131 | path.addClip() 132 | originalImage.draw(in: imageView.bounds) 133 | 134 | let croppedImage = UIGraphicsGetImageFromCurrentImageContext() 135 | UIGraphicsEndImageContext() 136 | return croppedImage 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /ImageFreeCut/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 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NSAppTransportSecurity 24 | 25 | NSAllowsArbitraryLoads 26 | 27 | 28 | UILaunchStoryboardName 29 | LaunchScreen 30 | UIMainStoryboardFile 31 | Main 32 | UIRequiredDeviceCapabilities 33 | 34 | armv7 35 | 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UISupportedInterfaceOrientations~ipad 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /ImageFreeCut/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // ImageFreeCut 4 | // 5 | // Created by Cem Olcay on 17/10/16. 6 | // Copyright © 2016 Mojilala. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ResultViewController: UIViewController { 12 | @IBOutlet weak var imageView: UIImageView? 13 | var resultImage: UIImage? 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | imageView?.image = resultImage 18 | } 19 | } 20 | 21 | class ViewController: UIViewController, ImageFreeCutViewDelegate { 22 | @IBOutlet weak var freeCutView: ImageFreeCutView? 23 | 24 | // MARK: Lifecycle 25 | override func viewDidLoad() { 26 | super.viewDidLoad() 27 | downloadTestCutImage() 28 | 29 | freeCutView?.delegate = self 30 | freeCutView?.imageCutShapeLayer.strokeColor = UIColor.green.cgColor 31 | freeCutView?.imageCutShapeLayer.lineWidth = 3 32 | } 33 | 34 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 35 | if let result = segue.destination as? ResultViewController, 36 | let image = sender as? UIImage, 37 | segue.identifier == "result" { 38 | result.resultImage = image 39 | } 40 | } 41 | 42 | // MARK: Demo Image 43 | private func downloadTestCutImage() { 44 | guard let url = URL(string: "http://static1.comicvine.com/uploads/scale_small/11/113509/4693444-6164752601-ben_a.jpg") else { return } 45 | 46 | let task = URLSession.shared.dataTask( 47 | with: url, 48 | completionHandler: { [weak self] data, _, _ in 49 | guard let data = data, let image = UIImage(data: data) else { return } 50 | self?.freeCutView?.imageToCut = image 51 | }) 52 | task.resume() 53 | } 54 | 55 | // MARK: ImageFreeCutViewDelegate 56 | func imageFreeCutView(_ imageFreeCutView: ImageFreeCutView, didCut image: UIImage?) { 57 | performSegue(withIdentifier: "result", sender: image) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2016, Cem Olcay 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a 4 | copy of this software and associated documentation files (the "Software"), 5 | to deal in the Software without restriction, including without limitation 6 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the 8 | Software is 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 16 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | DEALINGS IN THE SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | ImageFreeCut 2 | === 3 | 4 | A UIView subclass lets you draw a path over an image and crops that part. 5 | 6 | Demo 7 | ---- 8 | 9 | ![alt tag](https://github.com/cemolcay/ImageFreeCut/raw/master/Demo.gif) 10 | 11 | #### Try 12 | 13 | ``` sh 14 | pod try ImageFreeCut 15 | ``` 16 | 17 | Requirements 18 | ---- 19 | 20 | * Xcode 8+ 21 | * Swift 3+ 22 | * iOS 10+ 23 | 24 | Install 25 | ---- 26 | 27 | ``` ruby 28 | use_frameworks! 29 | pod 'ImageFreeCut' 30 | ``` 31 | 32 | Usage 33 | ---- 34 | 35 | ``` swift 36 | import ImageFreeCut 37 | ``` 38 | 39 | You can either use storyboard or create it programmatically like any UIView. 40 | Notice that view's class name is `ImageFreeCutView`. 41 | 42 | ``` 43 | open var imageToCut: UIImage? 44 | ``` 45 | Sets the image of `ImageFreeCutView`. You are going to crop that one. 46 | 47 | ``` 48 | open var imageView: UIImageView! 49 | ``` 50 | You can set the imageView's properties like `contentMode` directly from your `ImageFreeCutView` instance. 51 | 52 | ``` 53 | open var imageCutShapeLayer: CAShapeLayer! 54 | ``` 55 | You can set cut layer's properties like `strokeColor`, `fillColor`, `dashPattern` directly from your `ImageFreeCutView` instance. 56 | 57 | ImageFreeCutViewDelegate 58 | ---- 59 | Set your instance delegate and implement 60 | 61 | ``` 62 | func imageFreeCutView(_ imageFreeCutView: ImageFreeCutView, didCut image: UIImage?) 63 | ``` 64 | 65 | delegate method to retrive cropped image. 66 | --------------------------------------------------------------------------------