├── .gitignore ├── .travis.yml ├── Example ├── Podfile ├── SwiftGradients.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── SwiftGradients-Example.xcscheme ├── SwiftGradients.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── SwiftGradients │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── UIColorExtension.swift │ └── ViewController.swift └── Tests │ ├── Info.plist │ └── Tests.swift ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── CAGradientLayerExtension.swift ├── CALayerGradientsExtension.swift └── UIViewGradientsExtension.swift ├── Supporting Files ├── Info.plist └── SwiftGradients.h ├── SwiftGradients.podspec ├── SwiftGradients.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ ├── SwiftGradients.xcscheme │ └── SwiftGradientsTests.xcscheme ├── SwiftGradientsTests ├── Info.plist └── SwiftGradientsTests.swift └── gradient-preview.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | 20 | ## Other 21 | *.xccheckout 22 | *.moved-aside 23 | *.xcuserstate 24 | *.xcscmblueprint 25 | 26 | ## Obj-C/Swift specific 27 | *.hmap 28 | *.ipa 29 | 30 | # Swift Package Manager 31 | # 32 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 33 | # Packages/ 34 | .build/ 35 | 36 | # CocoaPods 37 | # 38 | # We recommend against adding the Pods directory to your .gitignore. However 39 | # you should judge for yourself, the pros and cons are mentioned at: 40 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 41 | # 42 | Pods/ 43 | Example/Pods/ 44 | 45 | # Carthage 46 | # 47 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 48 | # Carthage/Checkouts 49 | 50 | Carthage/Build 51 | 52 | **/.DS_Store 53 | 54 | # SPM 55 | .swiftpm/ 56 | -------------------------------------------------------------------------------- /.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: xcode11.5 6 | language: swift 7 | script: 8 | - set -o pipefail && xcodebuild test -enableCodeCoverage YES -scheme SwiftGradientsTests -destination 'platform=iOS Simulator,name=iPhone 11,OS=13.5' ONLY_ACTIVE_ARCH=NO | xcpretty 9 | - pod lib lint 10 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | target 'SwiftGradients_Example' do 4 | pod 'SwiftGradients', :path => '../' 5 | end 6 | -------------------------------------------------------------------------------- /Example/SwiftGradients.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; }; 11 | 9B0A604C24B3CCE100812410 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B0A604924B3CCE100812410 /* AppDelegate.swift */; }; 12 | 9B0A604D24B3CCE100812410 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9B0A604A24B3CCE100812410 /* Images.xcassets */; }; 13 | 9B0A604E24B3CCE100812410 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B0A604B24B3CCE100812410 /* ViewController.swift */; }; 14 | 9B0A605D24B3CEA600812410 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9B0A605124B3CD2000812410 /* LaunchScreen.xib */; }; 15 | 9B0A605E24B3CEA900812410 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9B0A605324B3CD2000812410 /* Main.storyboard */; }; 16 | 9B0A60D424B6602A00812410 /* UIColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B0A60D324B6602A00812410 /* UIColorExtension.swift */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXContainerItemProxy section */ 20 | 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */ = { 21 | isa = PBXContainerItemProxy; 22 | containerPortal = 607FACC81AFB9204008FA782 /* Project object */; 23 | proxyType = 1; 24 | remoteGlobalIDString = 607FACCF1AFB9204008FA782; 25 | remoteInfo = PagedLists; 26 | }; 27 | /* End PBXContainerItemProxy section */ 28 | 29 | /* Begin PBXFileReference section */ 30 | 4A26845499B9597435E23468 /* SwiftGradients.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = SwiftGradients.podspec; path = ../SwiftGradients.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 31 | 607FACD01AFB9204008FA782 /* SwiftGradients_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftGradients_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | 607FACE51AFB9204008FA782 /* SwiftGradients_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftGradients_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 34 | 607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; }; 35 | 6F926D8AFC30EDF831C02BBE /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 36 | 9B0A604924B3CCE100812410 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = SwiftGradients/AppDelegate.swift; sourceTree = SOURCE_ROOT; }; 37 | 9B0A604A24B3CCE100812410 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = SwiftGradients/Images.xcassets; sourceTree = SOURCE_ROOT; }; 38 | 9B0A604B24B3CCE100812410 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ViewController.swift; path = SwiftGradients/ViewController.swift; sourceTree = SOURCE_ROOT; }; 39 | 9B0A604F24B3CD0B00812410 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; name = Info.plist; path = SwiftGradients/Info.plist; sourceTree = SOURCE_ROOT; }; 40 | 9B0A605224B3CD2000812410 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = SwiftGradients/Base.lproj/LaunchScreen.xib; sourceTree = SOURCE_ROOT; }; 41 | 9B0A605424B3CD2000812410 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = SwiftGradients/Base.lproj/Main.storyboard; sourceTree = SOURCE_ROOT; }; 42 | 9B0A60D324B6602A00812410 /* UIColorExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColorExtension.swift; sourceTree = ""; }; 43 | FE30932B365BA88FDC62FF9E /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 44 | /* End PBXFileReference section */ 45 | 46 | /* Begin PBXFrameworksBuildPhase section */ 47 | 607FACCD1AFB9204008FA782 /* Frameworks */ = { 48 | isa = PBXFrameworksBuildPhase; 49 | buildActionMask = 2147483647; 50 | files = ( 51 | ); 52 | runOnlyForDeploymentPostprocessing = 0; 53 | }; 54 | 607FACE21AFB9204008FA782 /* Frameworks */ = { 55 | isa = PBXFrameworksBuildPhase; 56 | buildActionMask = 2147483647; 57 | files = ( 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXFrameworksBuildPhase section */ 62 | 63 | /* Begin PBXGroup section */ 64 | 607FACC71AFB9204008FA782 = { 65 | isa = PBXGroup; 66 | children = ( 67 | 607FACF51AFB993E008FA782 /* Podspec Metadata */, 68 | 607FACD21AFB9204008FA782 /* Example for SwiftGradients */, 69 | 607FACE81AFB9204008FA782 /* Tests */, 70 | 607FACD11AFB9204008FA782 /* Products */, 71 | 7AA3FEE233D418996AB40720 /* Pods */, 72 | ); 73 | sourceTree = ""; 74 | }; 75 | 607FACD11AFB9204008FA782 /* Products */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | 607FACD01AFB9204008FA782 /* SwiftGradients_Example.app */, 79 | 607FACE51AFB9204008FA782 /* SwiftGradients_Tests.xctest */, 80 | ); 81 | name = Products; 82 | sourceTree = ""; 83 | }; 84 | 607FACD21AFB9204008FA782 /* Example for SwiftGradients */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | 9B0A604B24B3CCE100812410 /* ViewController.swift */, 88 | 9B0A604924B3CCE100812410 /* AppDelegate.swift */, 89 | 9B0A604A24B3CCE100812410 /* Images.xcassets */, 90 | 9B0A605124B3CD2000812410 /* LaunchScreen.xib */, 91 | 9B0A605324B3CD2000812410 /* Main.storyboard */, 92 | 607FACD31AFB9204008FA782 /* Supporting Files */, 93 | 9B0A60D324B6602A00812410 /* UIColorExtension.swift */, 94 | ); 95 | name = "Example for SwiftGradients"; 96 | path = SwiftGradients; 97 | sourceTree = ""; 98 | }; 99 | 607FACD31AFB9204008FA782 /* Supporting Files */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | 9B0A604F24B3CD0B00812410 /* Info.plist */, 103 | ); 104 | name = "Supporting Files"; 105 | sourceTree = ""; 106 | }; 107 | 607FACE81AFB9204008FA782 /* Tests */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 607FACEB1AFB9204008FA782 /* Tests.swift */, 111 | 607FACE91AFB9204008FA782 /* Supporting Files */, 112 | ); 113 | path = Tests; 114 | sourceTree = ""; 115 | }; 116 | 607FACE91AFB9204008FA782 /* Supporting Files */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 607FACEA1AFB9204008FA782 /* Info.plist */, 120 | ); 121 | name = "Supporting Files"; 122 | sourceTree = ""; 123 | }; 124 | 607FACF51AFB993E008FA782 /* Podspec Metadata */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 4A26845499B9597435E23468 /* SwiftGradients.podspec */, 128 | FE30932B365BA88FDC62FF9E /* README.md */, 129 | 6F926D8AFC30EDF831C02BBE /* LICENSE */, 130 | ); 131 | name = "Podspec Metadata"; 132 | sourceTree = ""; 133 | }; 134 | 7AA3FEE233D418996AB40720 /* Pods */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | ); 138 | path = Pods; 139 | sourceTree = ""; 140 | }; 141 | /* End PBXGroup section */ 142 | 143 | /* Begin PBXNativeTarget section */ 144 | 607FACCF1AFB9204008FA782 /* SwiftGradients_Example */ = { 145 | isa = PBXNativeTarget; 146 | buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "SwiftGradients_Example" */; 147 | buildPhases = ( 148 | 607FACCC1AFB9204008FA782 /* Sources */, 149 | 607FACCD1AFB9204008FA782 /* Frameworks */, 150 | 607FACCE1AFB9204008FA782 /* Resources */, 151 | ); 152 | buildRules = ( 153 | ); 154 | dependencies = ( 155 | ); 156 | name = SwiftGradients_Example; 157 | productName = PagedLists; 158 | productReference = 607FACD01AFB9204008FA782 /* SwiftGradients_Example.app */; 159 | productType = "com.apple.product-type.application"; 160 | }; 161 | 607FACE41AFB9204008FA782 /* SwiftGradients_Tests */ = { 162 | isa = PBXNativeTarget; 163 | buildConfigurationList = 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "SwiftGradients_Tests" */; 164 | buildPhases = ( 165 | 607FACE11AFB9204008FA782 /* Sources */, 166 | 607FACE21AFB9204008FA782 /* Frameworks */, 167 | 607FACE31AFB9204008FA782 /* Resources */, 168 | ); 169 | buildRules = ( 170 | ); 171 | dependencies = ( 172 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */, 173 | ); 174 | name = SwiftGradients_Tests; 175 | productName = Tests; 176 | productReference = 607FACE51AFB9204008FA782 /* SwiftGradients_Tests.xctest */; 177 | productType = "com.apple.product-type.bundle.unit-test"; 178 | }; 179 | /* End PBXNativeTarget section */ 180 | 181 | /* Begin PBXProject section */ 182 | 607FACC81AFB9204008FA782 /* Project object */ = { 183 | isa = PBXProject; 184 | attributes = { 185 | LastSwiftUpdateCheck = 0830; 186 | LastUpgradeCheck = 0830; 187 | ORGANIZATIONNAME = CocoaPods; 188 | TargetAttributes = { 189 | 607FACCF1AFB9204008FA782 = { 190 | CreatedOnToolsVersion = 6.3.1; 191 | DevelopmentTeam = WNU857N39T; 192 | LastSwiftMigration = 0900; 193 | }; 194 | 607FACE41AFB9204008FA782 = { 195 | CreatedOnToolsVersion = 6.3.1; 196 | DevelopmentTeam = WNU857N39T; 197 | LastSwiftMigration = 0900; 198 | TestTargetID = 607FACCF1AFB9204008FA782; 199 | }; 200 | }; 201 | }; 202 | buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "SwiftGradients" */; 203 | compatibilityVersion = "Xcode 3.2"; 204 | developmentRegion = English; 205 | hasScannedForEncodings = 0; 206 | knownRegions = ( 207 | English, 208 | en, 209 | Base, 210 | ); 211 | mainGroup = 607FACC71AFB9204008FA782; 212 | productRefGroup = 607FACD11AFB9204008FA782 /* Products */; 213 | projectDirPath = ""; 214 | projectRoot = ""; 215 | targets = ( 216 | 607FACCF1AFB9204008FA782 /* SwiftGradients_Example */, 217 | 607FACE41AFB9204008FA782 /* SwiftGradients_Tests */, 218 | ); 219 | }; 220 | /* End PBXProject section */ 221 | 222 | /* Begin PBXResourcesBuildPhase section */ 223 | 607FACCE1AFB9204008FA782 /* Resources */ = { 224 | isa = PBXResourcesBuildPhase; 225 | buildActionMask = 2147483647; 226 | files = ( 227 | 9B0A604D24B3CCE100812410 /* Images.xcassets in Resources */, 228 | 9B0A605E24B3CEA900812410 /* Main.storyboard in Resources */, 229 | 9B0A605D24B3CEA600812410 /* LaunchScreen.xib in Resources */, 230 | ); 231 | runOnlyForDeploymentPostprocessing = 0; 232 | }; 233 | 607FACE31AFB9204008FA782 /* Resources */ = { 234 | isa = PBXResourcesBuildPhase; 235 | buildActionMask = 2147483647; 236 | files = ( 237 | ); 238 | runOnlyForDeploymentPostprocessing = 0; 239 | }; 240 | /* End PBXResourcesBuildPhase section */ 241 | 242 | /* Begin PBXSourcesBuildPhase section */ 243 | 607FACCC1AFB9204008FA782 /* Sources */ = { 244 | isa = PBXSourcesBuildPhase; 245 | buildActionMask = 2147483647; 246 | files = ( 247 | 9B0A604C24B3CCE100812410 /* AppDelegate.swift in Sources */, 248 | 9B0A604E24B3CCE100812410 /* ViewController.swift in Sources */, 249 | 9B0A60D424B6602A00812410 /* UIColorExtension.swift in Sources */, 250 | ); 251 | runOnlyForDeploymentPostprocessing = 0; 252 | }; 253 | 607FACE11AFB9204008FA782 /* Sources */ = { 254 | isa = PBXSourcesBuildPhase; 255 | buildActionMask = 2147483647; 256 | files = ( 257 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */, 258 | ); 259 | runOnlyForDeploymentPostprocessing = 0; 260 | }; 261 | /* End PBXSourcesBuildPhase section */ 262 | 263 | /* Begin PBXTargetDependency section */ 264 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */ = { 265 | isa = PBXTargetDependency; 266 | target = 607FACCF1AFB9204008FA782 /* SwiftGradients_Example */; 267 | targetProxy = 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */; 268 | }; 269 | /* End PBXTargetDependency section */ 270 | 271 | /* Begin PBXVariantGroup section */ 272 | 9B0A605124B3CD2000812410 /* LaunchScreen.xib */ = { 273 | isa = PBXVariantGroup; 274 | children = ( 275 | 9B0A605224B3CD2000812410 /* Base */, 276 | ); 277 | name = LaunchScreen.xib; 278 | sourceTree = ""; 279 | }; 280 | 9B0A605324B3CD2000812410 /* Main.storyboard */ = { 281 | isa = PBXVariantGroup; 282 | children = ( 283 | 9B0A605424B3CD2000812410 /* Base */, 284 | ); 285 | name = Main.storyboard; 286 | sourceTree = ""; 287 | }; 288 | /* End PBXVariantGroup section */ 289 | 290 | /* Begin XCBuildConfiguration section */ 291 | 607FACED1AFB9204008FA782 /* Debug */ = { 292 | isa = XCBuildConfiguration; 293 | buildSettings = { 294 | ALWAYS_SEARCH_USER_PATHS = NO; 295 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 296 | CLANG_CXX_LIBRARY = "libc++"; 297 | CLANG_ENABLE_MODULES = YES; 298 | CLANG_ENABLE_OBJC_ARC = YES; 299 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 300 | CLANG_WARN_BOOL_CONVERSION = YES; 301 | CLANG_WARN_COMMA = YES; 302 | CLANG_WARN_CONSTANT_CONVERSION = YES; 303 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 304 | CLANG_WARN_EMPTY_BODY = YES; 305 | CLANG_WARN_ENUM_CONVERSION = YES; 306 | CLANG_WARN_INFINITE_RECURSION = YES; 307 | CLANG_WARN_INT_CONVERSION = YES; 308 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 309 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 310 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 311 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 312 | CLANG_WARN_STRICT_PROTOTYPES = YES; 313 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 314 | CLANG_WARN_UNREACHABLE_CODE = YES; 315 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 316 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 317 | COPY_PHASE_STRIP = NO; 318 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 319 | ENABLE_STRICT_OBJC_MSGSEND = YES; 320 | ENABLE_TESTABILITY = YES; 321 | GCC_C_LANGUAGE_STANDARD = gnu99; 322 | GCC_DYNAMIC_NO_PIC = NO; 323 | GCC_NO_COMMON_BLOCKS = YES; 324 | GCC_OPTIMIZATION_LEVEL = 0; 325 | GCC_PREPROCESSOR_DEFINITIONS = ( 326 | "DEBUG=1", 327 | "$(inherited)", 328 | ); 329 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 330 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 331 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 332 | GCC_WARN_UNDECLARED_SELECTOR = YES; 333 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 334 | GCC_WARN_UNUSED_FUNCTION = YES; 335 | GCC_WARN_UNUSED_VARIABLE = YES; 336 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 337 | MTL_ENABLE_DEBUG_INFO = YES; 338 | ONLY_ACTIVE_ARCH = YES; 339 | SDKROOT = iphoneos; 340 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 341 | }; 342 | name = Debug; 343 | }; 344 | 607FACEE1AFB9204008FA782 /* Release */ = { 345 | isa = XCBuildConfiguration; 346 | buildSettings = { 347 | ALWAYS_SEARCH_USER_PATHS = NO; 348 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 349 | CLANG_CXX_LIBRARY = "libc++"; 350 | CLANG_ENABLE_MODULES = YES; 351 | CLANG_ENABLE_OBJC_ARC = YES; 352 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 353 | CLANG_WARN_BOOL_CONVERSION = YES; 354 | CLANG_WARN_COMMA = YES; 355 | CLANG_WARN_CONSTANT_CONVERSION = YES; 356 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 357 | CLANG_WARN_EMPTY_BODY = YES; 358 | CLANG_WARN_ENUM_CONVERSION = YES; 359 | CLANG_WARN_INFINITE_RECURSION = YES; 360 | CLANG_WARN_INT_CONVERSION = YES; 361 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 362 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 363 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 364 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 365 | CLANG_WARN_STRICT_PROTOTYPES = YES; 366 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 367 | CLANG_WARN_UNREACHABLE_CODE = YES; 368 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 369 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 370 | COPY_PHASE_STRIP = NO; 371 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 372 | ENABLE_NS_ASSERTIONS = NO; 373 | ENABLE_STRICT_OBJC_MSGSEND = YES; 374 | GCC_C_LANGUAGE_STANDARD = gnu99; 375 | GCC_NO_COMMON_BLOCKS = YES; 376 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 377 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 378 | GCC_WARN_UNDECLARED_SELECTOR = YES; 379 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 380 | GCC_WARN_UNUSED_FUNCTION = YES; 381 | GCC_WARN_UNUSED_VARIABLE = YES; 382 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 383 | MTL_ENABLE_DEBUG_INFO = NO; 384 | SDKROOT = iphoneos; 385 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 386 | VALIDATE_PRODUCT = YES; 387 | }; 388 | name = Release; 389 | }; 390 | 607FACF01AFB9204008FA782 /* Debug */ = { 391 | isa = XCBuildConfiguration; 392 | buildSettings = { 393 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 394 | DEVELOPMENT_TEAM = WNU857N39T; 395 | INFOPLIST_FILE = "$(SRCROOT)/SwiftGradients/Info.plist"; 396 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 397 | MODULE_NAME = ExampleApp; 398 | PRODUCT_BUNDLE_IDENTIFIER = "com.rootstrap.SwiftGradients-Example"; 399 | PRODUCT_NAME = "$(TARGET_NAME)"; 400 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 401 | SWIFT_VERSION = 5.0; 402 | TARGETED_DEVICE_FAMILY = 1; 403 | }; 404 | name = Debug; 405 | }; 406 | 607FACF11AFB9204008FA782 /* Release */ = { 407 | isa = XCBuildConfiguration; 408 | buildSettings = { 409 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 410 | DEVELOPMENT_TEAM = WNU857N39T; 411 | INFOPLIST_FILE = "$(SRCROOT)/SwiftGradients/Info.plist"; 412 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 413 | MODULE_NAME = ExampleApp; 414 | PRODUCT_BUNDLE_IDENTIFIER = "com.rootstrap.SwiftGradients-Example"; 415 | PRODUCT_NAME = "$(TARGET_NAME)"; 416 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 417 | SWIFT_VERSION = 5.0; 418 | TARGETED_DEVICE_FAMILY = 1; 419 | }; 420 | name = Release; 421 | }; 422 | 607FACF31AFB9204008FA782 /* Debug */ = { 423 | isa = XCBuildConfiguration; 424 | buildSettings = { 425 | DEVELOPMENT_TEAM = WNU857N39T; 426 | FRAMEWORK_SEARCH_PATHS = ( 427 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 428 | "$(inherited)", 429 | ); 430 | GCC_PREPROCESSOR_DEFINITIONS = ( 431 | "DEBUG=1", 432 | "$(inherited)", 433 | ); 434 | INFOPLIST_FILE = Tests/Info.plist; 435 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 436 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 437 | PRODUCT_NAME = "$(TARGET_NAME)"; 438 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 439 | SWIFT_VERSION = 5.0; 440 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftGradients_Example.app/SwiftGradients_Example"; 441 | }; 442 | name = Debug; 443 | }; 444 | 607FACF41AFB9204008FA782 /* Release */ = { 445 | isa = XCBuildConfiguration; 446 | buildSettings = { 447 | DEVELOPMENT_TEAM = WNU857N39T; 448 | FRAMEWORK_SEARCH_PATHS = ( 449 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 450 | "$(inherited)", 451 | ); 452 | INFOPLIST_FILE = Tests/Info.plist; 453 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 454 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 455 | PRODUCT_NAME = "$(TARGET_NAME)"; 456 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 457 | SWIFT_VERSION = 5.0; 458 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftGradients_Example.app/SwiftGradients_Example"; 459 | }; 460 | name = Release; 461 | }; 462 | /* End XCBuildConfiguration section */ 463 | 464 | /* Begin XCConfigurationList section */ 465 | 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "SwiftGradients" */ = { 466 | isa = XCConfigurationList; 467 | buildConfigurations = ( 468 | 607FACED1AFB9204008FA782 /* Debug */, 469 | 607FACEE1AFB9204008FA782 /* Release */, 470 | ); 471 | defaultConfigurationIsVisible = 0; 472 | defaultConfigurationName = Release; 473 | }; 474 | 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "SwiftGradients_Example" */ = { 475 | isa = XCConfigurationList; 476 | buildConfigurations = ( 477 | 607FACF01AFB9204008FA782 /* Debug */, 478 | 607FACF11AFB9204008FA782 /* Release */, 479 | ); 480 | defaultConfigurationIsVisible = 0; 481 | defaultConfigurationName = Release; 482 | }; 483 | 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "SwiftGradients_Tests" */ = { 484 | isa = XCConfigurationList; 485 | buildConfigurations = ( 486 | 607FACF31AFB9204008FA782 /* Debug */, 487 | 607FACF41AFB9204008FA782 /* Release */, 488 | ); 489 | defaultConfigurationIsVisible = 0; 490 | defaultConfigurationName = Release; 491 | }; 492 | /* End XCConfigurationList section */ 493 | }; 494 | rootObject = 607FACC81AFB9204008FA782 /* Project object */; 495 | } 496 | -------------------------------------------------------------------------------- /Example/SwiftGradients.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/SwiftGradients.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/SwiftGradients.xcodeproj/xcshareddata/xcschemes/SwiftGradients-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 51 | 52 | 53 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 76 | 78 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /Example/SwiftGradients.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/SwiftGradients.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/SwiftGradients/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | @UIApplicationMain 4 | class AppDelegate: UIResponder, UIApplicationDelegate { 5 | 6 | var window: UIWindow? 7 | 8 | func application( 9 | _ application: UIApplication, 10 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 11 | ) -> Bool { 12 | // Override point for customization after application launch. 13 | return true 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /Example/SwiftGradients/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example/SwiftGradients/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 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example/SwiftGradients/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/SwiftGradients/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 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Example/SwiftGradients/UIColorExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColorExtension.swift 3 | // SwiftGradients_Example 4 | // 5 | // Created by German on 7/8/20. 6 | // Copyright © 2020 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIColor { 12 | static let beachBlue = UIColor( 13 | red: 3 / 255, 14 | green: 117 / 255, 15 | blue: 251 / 255, 16 | alpha: 1 17 | ) 18 | 19 | static let limeGreen = UIColor( 20 | red: 19 / 255, 21 | green: 253 / 255, 22 | blue: 153 / 255, 23 | alpha: 1 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /Example/SwiftGradients/ViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import SwiftGradients 3 | 4 | class ViewController: UIViewController { 5 | 6 | override func viewDidLoad() { 7 | super.viewDidLoad() 8 | view.addGradient( 9 | colors: [.beachBlue, .limeGreen], 10 | direction: .bottomToTop 11 | ) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /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 | 3 | class Tests: XCTestCase { 4 | 5 | override func setUp() { 6 | super.setUp() 7 | // Put setup code here. This method is called before the invocation of each test method in the class. 8 | } 9 | 10 | override func tearDown() { 11 | // Put teardown code here. This method is called after the invocation of each test method in the class. 12 | super.tearDown() 13 | } 14 | 15 | func testExample() { 16 | // This is an example of a functional test case. 17 | XCTAssert(true, "Pass") 18 | } 19 | 20 | func testPerformanceExample() { 21 | // This is an example of a performance test case. 22 | self.measure() { 23 | // Put the code you want to measure the time of here. 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Rootstrap Inc. 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 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "SwiftGradients", 8 | platforms: [.iOS(.v9)], 9 | products: [ 10 | .library(name: "SwiftGradients", targets: ["SwiftGradients"]), 11 | ], 12 | targets: [ 13 | .target(name: "SwiftGradients", dependencies: [], path: "Sources") 14 | ] 15 | ) 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # SwiftGradients 3 | 4 | [![CI Status](https://img.shields.io/travis/rootstrap/SwiftGradients.svg?style=flat)](https://travis-ci.org/rootstrap/SwiftGradients) 5 | [![Version](https://img.shields.io/cocoapods/v/SwiftGradients.svg?style=flat)](https://cocoapods.org/pods/SwiftGradients) 6 | [![License](https://img.shields.io/cocoapods/l/SwiftGradients.svg?style=flat)](https://cocoapods.org/pods/SwiftGradients) 7 | [![Platform](https://img.shields.io/cocoapods/p/SwiftGradients.svg?style=flat)](https://cocoapods.org/pods/SwiftGradients) 8 | [![Carthage](https://img.shields.io/badge/Carthage-compatible-success)](#installation) 9 | [![SPM](https://img.shields.io/badge/SPM-compatible-success)](#installation) 10 | [![Swift Version](https://img.shields.io/badge/Swift%20Version-5.2-orange)](https://cocoapods.org/pods/SwiftGradients) 11 | 12 | ## What is it? 13 | 14 | **SwiftGradients** gives you useful extensions for `UIViews` and `CALayer` classes to add beautiful color gradients. 15 | 16 | ## Installation 17 | 18 | #### 1. Cocoapods 19 | 20 | ```ruby 21 | 22 | pod 'SwiftGradients', '~> 1.0.0' 23 | ``` 24 | 25 | #### 2. Carthage 26 | 27 | [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. 28 | Add the following line to your `Cartfile` and follow the [installation instructions](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application). 29 | 30 | ``` 31 | github "rootstrap/SwiftGradients" ~> 1.0.0 32 | ``` 33 | 34 | #### 3. Swift Package Manager 35 | 36 | - In XCode 11, go to File -> Swift Packages -> Add Package Dependency. 37 | - Enter the repo URL (https://github.com/rootstrap/SwiftGradients) and click Next. 38 | - Select the version rule desired (you can specify a version number, branch or commit) and click Next. 39 | - Finally, select the target where you want to use the framework. 40 | 41 | That should be it. **SwiftGradients** should appear in the navigation panel as a dependency and the framework will be linked automatically to your target. 42 | 43 | 44 | **Note:** It is always recommended to lock your external libraries to a specific version. 45 | 46 | ## Usage 47 | 48 | #### 1. Adding gradients to your subviews 49 | 50 | ```swift 51 | let gradientLayer = view.addGradient( 52 | colors: [.blue, .green], 53 | direction: .topToBottom 54 | ) 55 | ``` 56 | 57 | #### 2. Customize options for your gradient 58 | 59 | You can set a custom angle(measured in degrees and anti-clockwise), rather that one of the four predefined directions. 60 | Also, you can provide the color stop locations for better accuracy. 61 | 62 | ```swift 63 | let gradientLayer = view.addGradient( 64 | colors: [.blue, .green, .black], 65 | angle: 45 66 | locations: [0, 50, 75] 67 | ) 68 | ``` 69 | 70 | #### Voilà 71 | 72 | 73 |
74 | 75 | ## Example 76 | 77 | To run the example project, clone the repo, and run `pod install` from the Example directory first. 78 | 79 | 80 | ## License 81 | 82 | **SwiftGradients** is available under the MIT license. See the LICENSE file for more info. 83 | 84 | [](http://www.rootstrap.com) 85 | -------------------------------------------------------------------------------- /Sources/CAGradientLayerExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CAGradientLayerExtension.swift 3 | // SwiftGradients 4 | // 5 | // Created by German Lopez on 7/8/20. 6 | // Copyright © 2020 Rootstrap. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | #if canImport(UIKit) 11 | import UIKit 12 | 13 | extension CAGradientLayer { 14 | class func startPointFor(_ angle: Double) -> CGPoint { 15 | if let defaultDirection = GradientDirection(rawValue: angle) { 16 | switch defaultDirection { 17 | case .topToBottom: 18 | return CGPoint(x: 0.5, y: 0.0) 19 | case .bottomToTop: 20 | return CGPoint(x: 0.5, y: 1.0) 21 | case .leftToRight: 22 | return CGPoint(x: 0.0, y: 0.5) 23 | default: 24 | return CGPoint(x: 1.0, y: 0.5) 25 | } 26 | } 27 | return pointWithAngle(angle) 28 | } 29 | 30 | class func endPointFor(_ angle: Double) -> CGPoint { 31 | if let defaultDirection = GradientDirection(rawValue: angle) { 32 | switch defaultDirection { 33 | case .topToBottom: 34 | return CGPoint(x: 0.5, y: 1.0) 35 | case .bottomToTop: 36 | return CGPoint(x: 0.5, y: 0.0) 37 | case .leftToRight: 38 | return CGPoint(x: 1.0, y: 0.5) 39 | default: 40 | return CGPoint(x: 0.0, y: 0.5) 41 | } 42 | } 43 | return pointWithAngle(angle, isStartPoint: false) 44 | } 45 | 46 | /// **pointWithAngle**: Helper for CAGradientLayer's start and endPoint given an angle in degrees 47 | /// - Parameter **angle** The desired angle in degrees and measured anti-clockwise. 48 | /// - Parameter **isStartPoint** A boolean indicating which point you need. 49 | /// - Returns: The initial or ending CGPoint for a CAGradientLayer within the Unit Cordinate System. 50 | private class func pointWithAngle( 51 | _ angle: Double, 52 | isStartPoint: Bool = true 53 | ) -> CGPoint { 54 | // negative angles not allowed 55 | var positiveAngle = angle < 0 ? angle * -1.0 : angle 56 | var y1: Double, y2: Double, x1: Double, x2: Double 57 | 58 | if // ranges when we know Y values 59 | (positiveAngle >= 45 && positiveAngle <= 135) || 60 | (positiveAngle >= 225 && positiveAngle <= 315) 61 | { 62 | y1 = positiveAngle < 180 ? 0.0 : 1.0 63 | y2 = 1.0 - y1 //opposite to start Y 64 | x1 = positiveAngle >= 45 && positiveAngle <= 135 ? 65 | 1.5 - positiveAngle / 90 : 66 | abs(2.5 - positiveAngle / 90) 67 | x2 = 1.0-x1 //opposite to start X 68 | } else { // ranges when we know X values 69 | x1 = positiveAngle < 45 || positiveAngle >= 315 ? 1.0 : 0.0 70 | x2 = 1.0 - x1 71 | if positiveAngle > 135 && positiveAngle < 225 { 72 | y2 = abs(2.5 - positiveAngle / 90) 73 | y1 = 1.0 - y2 74 | } else { // Range 0-45 315-360 75 | //Turn this ranges into one single 90 degrees range 76 | positiveAngle = positiveAngle >= 0 && positiveAngle <= 45 ? 77 | 45.0 - positiveAngle : 78 | 360 - positiveAngle + 45 79 | y1 = positiveAngle / 90 80 | y2 = 1.0 - y1 81 | } 82 | } 83 | return isStartPoint ? CGPoint(x: x1, y: y1) : CGPoint(x: x2, y: y2) 84 | } 85 | } 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /Sources/CALayerGradientsExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CALayerGradientsExtension.swift 3 | // SwiftGradients 4 | // 5 | // Created by German Lopez on 7/8/20. 6 | // Copyright © 2020 Rootstrap. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | #if canImport(UIKit) 11 | import UIKit 12 | 13 | public extension CALayer { 14 | @discardableResult 15 | func addGradient( 16 | colors: [UIColor], 17 | direction: GradientDirection = .topToBottom, 18 | locations: [Int] = [] 19 | ) -> CAGradientLayer { 20 | return addGradient( 21 | colors: colors, 22 | startPoint: CAGradientLayer.startPointFor(direction.rawValue), 23 | endPoint: CAGradientLayer.endPointFor(direction.rawValue), 24 | locations: locations 25 | ) 26 | } 27 | 28 | /// Angle param is measured anti-clockwise 29 | @discardableResult 30 | func addGradient( 31 | colors: [UIColor], 32 | angle: Double, 33 | locations: [Int] = [] 34 | ) -> CAGradientLayer { 35 | return addGradient( 36 | colors: colors, 37 | startPoint: CAGradientLayer.startPointFor(angle), 38 | endPoint: CAGradientLayer.endPointFor(angle), 39 | locations: locations 40 | ) 41 | } 42 | 43 | /// This method will add a gradient sublayer in background with the given colors, 44 | /// points and locations for color stops(in %[0-100]). 45 | @discardableResult 46 | func addGradient( 47 | colors: [UIColor], 48 | startPoint: CGPoint, 49 | endPoint: CGPoint, 50 | locations: [Int] = [] 51 | ) -> CAGradientLayer { 52 | var gradient: CAGradientLayer 53 | if let current = sublayers?.first as? CAGradientLayer { 54 | gradient = current 55 | } else { 56 | gradient = CAGradientLayer() 57 | insertSublayer(gradient, at: 0) 58 | } 59 | gradient.frame = bounds 60 | gradient.colors = colors.map { $0.cgColor } 61 | 62 | gradient.startPoint = startPoint 63 | gradient.endPoint = endPoint 64 | if !locations.isEmpty { 65 | gradient.locations = locations.map { NSNumber(value: Float($0) / 100.0) } 66 | } 67 | return gradient 68 | } 69 | } 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /Sources/UIViewGradientsExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GeneralHelper.swift 3 | // swift-base 4 | // 5 | // Created by German Lopez on 2/19/16. 6 | // Copyright © 2016 Rootstrap Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | #if canImport(UIKit) 11 | import UIKit 12 | 13 | public enum GradientDirection: Double { 14 | case topToBottom = 90.0 15 | case bottomToTop = 270.0 16 | case leftToRight = 180.0 17 | case rightToLeft = 0.0 18 | } 19 | 20 | public extension UIView { 21 | @discardableResult 22 | func addGradient( 23 | colors: [UIColor], 24 | direction: GradientDirection = .topToBottom, 25 | locations: [Int] = [] 26 | ) -> CAGradientLayer { 27 | return addGradient( 28 | colors: colors, 29 | startPoint: CAGradientLayer.startPointFor(direction.rawValue), 30 | endPoint: CAGradientLayer.endPointFor(direction.rawValue), 31 | locations: locations 32 | ) 33 | } 34 | 35 | @discardableResult 36 | func addGradient( 37 | colors: [UIColor], 38 | angle: Double, 39 | locations: [Int] = [] 40 | ) -> CAGradientLayer { 41 | return addGradient( 42 | colors: colors, 43 | startPoint: CAGradientLayer.startPointFor(angle), 44 | endPoint: CAGradientLayer.endPointFor(angle), 45 | locations: locations 46 | ) 47 | } 48 | 49 | @discardableResult 50 | func addGradient( 51 | colors: [UIColor], 52 | startPoint: CGPoint, 53 | endPoint: CGPoint, 54 | locations: [Int] = [] 55 | ) -> CAGradientLayer { 56 | return layer.addGradient( 57 | colors: colors, 58 | startPoint: startPoint, 59 | endPoint: endPoint, 60 | locations: locations 61 | ) 62 | } 63 | } 64 | 65 | public extension Array where Element: UIView { 66 | func addGradient( 67 | withColors colors: [UIColor], 68 | direction: GradientDirection = .topToBottom, 69 | locations: [Int] = [] 70 | ) { 71 | for view in self { 72 | view.addGradient(colors: colors, direction: direction, locations: locations) 73 | } 74 | } 75 | 76 | func addGradient(colors: [UIColor], angle: Double, locations: [Int] = []) { 77 | for view in self { 78 | view.addGradient(colors: colors, angle: angle, locations: locations) 79 | } 80 | } 81 | } 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /Supporting Files/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /Supporting Files/SwiftGradients.h: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftGradients.h 3 | // 4 | // 5 | // Created by Rootstrap Inc. on 6/11/20. 6 | // Copyright © 2020 Rootstrap Inc.. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for SwiftGradients. 12 | FOUNDATION_EXPORT double SwiftGradientsVersionNumber; 13 | 14 | //! Project version string for SwiftGradients. 15 | FOUNDATION_EXPORT const unsigned char SwiftGradientsVersionString[]; 16 | -------------------------------------------------------------------------------- /SwiftGradients.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'SwiftGradients' 3 | s.version = '1.0.0' 4 | s.summary = 'Color gradients for UIView and CALayer.' 5 | 6 | s.description = 'SwiftGradients gives you useful extensions for UIViews and CALayer classes to add beautiful color gradients.' 7 | 8 | s.homepage = 'https://github.com/rootstrap/SwiftGradients' 9 | s.license = { :type => 'MIT', :file => 'LICENSE' } 10 | s.author = { 'German Lopez' => 'german@rootstrap.com' } 11 | s.source = { :git => 'https://github.com/rootstrap/SwiftGradients.git', 12 | :tag => s.version.to_s 13 | } 14 | s.social_media_url = 'https://www.rootstrap.com/' 15 | 16 | s.ios.deployment_target = '9.3' 17 | 18 | s.source_files = 'Sources/**/*' 19 | s.frameworks = 'UIKit' 20 | s.swift_version = '5.2' 21 | end 22 | -------------------------------------------------------------------------------- /SwiftGradients.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 9B0A60CE24B658D000812410 /* UIViewGradientsExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B0A60CD24B658D000812410 /* UIViewGradientsExtension.swift */; }; 11 | 9B0A60D024B6595000812410 /* CALayerGradientsExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B0A60CF24B6595000812410 /* CALayerGradientsExtension.swift */; }; 12 | 9B0A60D224B65A6D00812410 /* CAGradientLayerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B0A60D124B65A6D00812410 /* CAGradientLayerExtension.swift */; }; 13 | 9B0A60E424B7566000812410 /* SwiftGradientsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B0A60E324B7566000812410 /* SwiftGradientsTests.swift */; }; 14 | 9B0A60E624B7566000812410 /* SwiftGradients.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9BFC1182249270E800AECF6C /* SwiftGradients.framework */; platformFilter = ios; }; 15 | 9BFC1187249270E800AECF6C /* SwiftGradients.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BFC1185249270E800AECF6C /* SwiftGradients.h */; settings = {ATTRIBUTES = (Public, ); }; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXContainerItemProxy section */ 19 | 9B0A60E724B7566000812410 /* PBXContainerItemProxy */ = { 20 | isa = PBXContainerItemProxy; 21 | containerPortal = 9BFC1179249270E800AECF6C /* Project object */; 22 | proxyType = 1; 23 | remoteGlobalIDString = 9BFC1181249270E800AECF6C; 24 | remoteInfo = SwiftGradients; 25 | }; 26 | /* End PBXContainerItemProxy section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | 9B0A60CD24B658D000812410 /* UIViewGradientsExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewGradientsExtension.swift; sourceTree = ""; }; 30 | 9B0A60CF24B6595000812410 /* CALayerGradientsExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CALayerGradientsExtension.swift; sourceTree = ""; }; 31 | 9B0A60D124B65A6D00812410 /* CAGradientLayerExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CAGradientLayerExtension.swift; sourceTree = ""; }; 32 | 9B0A60E124B7566000812410 /* SwiftGradientsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftGradientsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | 9B0A60E324B7566000812410 /* SwiftGradientsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftGradientsTests.swift; sourceTree = ""; }; 34 | 9B0A60E524B7566000812410 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 35 | 9BFC1182249270E800AECF6C /* SwiftGradients.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftGradients.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | 9BFC1185249270E800AECF6C /* SwiftGradients.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftGradients.h; sourceTree = ""; }; 37 | 9BFC1186249270E800AECF6C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 38 | 9BFC119F2492902000AECF6C /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 39 | 9BFC11A124929F8500AECF6C /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 40 | /* End PBXFileReference section */ 41 | 42 | /* Begin PBXFrameworksBuildPhase section */ 43 | 9B0A60DE24B7566000812410 /* Frameworks */ = { 44 | isa = PBXFrameworksBuildPhase; 45 | buildActionMask = 2147483647; 46 | files = ( 47 | 9B0A60E624B7566000812410 /* SwiftGradients.framework in Frameworks */, 48 | ); 49 | runOnlyForDeploymentPostprocessing = 0; 50 | }; 51 | 9BFC117F249270E800AECF6C /* Frameworks */ = { 52 | isa = PBXFrameworksBuildPhase; 53 | buildActionMask = 2147483647; 54 | files = ( 55 | ); 56 | runOnlyForDeploymentPostprocessing = 0; 57 | }; 58 | /* End PBXFrameworksBuildPhase section */ 59 | 60 | /* Begin PBXGroup section */ 61 | 9B0A60E224B7566000812410 /* SwiftGradientsTests */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | 9B0A60E324B7566000812410 /* SwiftGradientsTests.swift */, 65 | 9B0A60E524B7566000812410 /* Info.plist */, 66 | ); 67 | path = SwiftGradientsTests; 68 | sourceTree = ""; 69 | }; 70 | 9BFC1178249270E800AECF6C = { 71 | isa = PBXGroup; 72 | children = ( 73 | 9BFC11A124929F8500AECF6C /* Package.swift */, 74 | 9BFC119F2492902000AECF6C /* README.md */, 75 | 9BFC118E249273CE00AECF6C /* Sources */, 76 | 9BFC1184249270E800AECF6C /* Supporting Files */, 77 | 9B0A60E224B7566000812410 /* SwiftGradientsTests */, 78 | 9BFC1183249270E800AECF6C /* Products */, 79 | ); 80 | sourceTree = ""; 81 | }; 82 | 9BFC1183249270E800AECF6C /* Products */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 9BFC1182249270E800AECF6C /* SwiftGradients.framework */, 86 | 9B0A60E124B7566000812410 /* SwiftGradientsTests.xctest */, 87 | ); 88 | name = Products; 89 | sourceTree = ""; 90 | }; 91 | 9BFC1184249270E800AECF6C /* Supporting Files */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 9BFC1185249270E800AECF6C /* SwiftGradients.h */, 95 | 9BFC1186249270E800AECF6C /* Info.plist */, 96 | ); 97 | path = "Supporting Files"; 98 | sourceTree = ""; 99 | }; 100 | 9BFC118E249273CE00AECF6C /* Sources */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 9B0A60CD24B658D000812410 /* UIViewGradientsExtension.swift */, 104 | 9B0A60CF24B6595000812410 /* CALayerGradientsExtension.swift */, 105 | 9B0A60D124B65A6D00812410 /* CAGradientLayerExtension.swift */, 106 | ); 107 | path = Sources; 108 | sourceTree = ""; 109 | }; 110 | /* End PBXGroup section */ 111 | 112 | /* Begin PBXHeadersBuildPhase section */ 113 | 9BFC117D249270E800AECF6C /* Headers */ = { 114 | isa = PBXHeadersBuildPhase; 115 | buildActionMask = 2147483647; 116 | files = ( 117 | 9BFC1187249270E800AECF6C /* SwiftGradients.h in Headers */, 118 | ); 119 | runOnlyForDeploymentPostprocessing = 0; 120 | }; 121 | /* End PBXHeadersBuildPhase section */ 122 | 123 | /* Begin PBXNativeTarget section */ 124 | 9B0A60E024B7566000812410 /* SwiftGradientsTests */ = { 125 | isa = PBXNativeTarget; 126 | buildConfigurationList = 9B0A60E924B7566000812410 /* Build configuration list for PBXNativeTarget "SwiftGradientsTests" */; 127 | buildPhases = ( 128 | 9B0A60DD24B7566000812410 /* Sources */, 129 | 9B0A60DE24B7566000812410 /* Frameworks */, 130 | 9B0A60DF24B7566000812410 /* Resources */, 131 | ); 132 | buildRules = ( 133 | ); 134 | dependencies = ( 135 | 9B0A60E824B7566000812410 /* PBXTargetDependency */, 136 | ); 137 | name = SwiftGradientsTests; 138 | productName = SwiftGradientsTests; 139 | productReference = 9B0A60E124B7566000812410 /* SwiftGradientsTests.xctest */; 140 | productType = "com.apple.product-type.bundle.unit-test"; 141 | }; 142 | 9BFC1181249270E800AECF6C /* SwiftGradients */ = { 143 | isa = PBXNativeTarget; 144 | buildConfigurationList = 9BFC118A249270E800AECF6C /* Build configuration list for PBXNativeTarget "SwiftGradients" */; 145 | buildPhases = ( 146 | 9BFC117D249270E800AECF6C /* Headers */, 147 | 9BFC117E249270E800AECF6C /* Sources */, 148 | 9BFC117F249270E800AECF6C /* Frameworks */, 149 | 9BFC1180249270E800AECF6C /* Resources */, 150 | ); 151 | buildRules = ( 152 | ); 153 | dependencies = ( 154 | ); 155 | name = SwiftGradients; 156 | productName = PagedLists; 157 | productReference = 9BFC1182249270E800AECF6C /* SwiftGradients.framework */; 158 | productType = "com.apple.product-type.framework"; 159 | }; 160 | /* End PBXNativeTarget section */ 161 | 162 | /* Begin PBXProject section */ 163 | 9BFC1179249270E800AECF6C /* Project object */ = { 164 | isa = PBXProject; 165 | attributes = { 166 | LastSwiftUpdateCheck = 1150; 167 | LastUpgradeCheck = 1150; 168 | ORGANIZATIONNAME = Rootstrap; 169 | TargetAttributes = { 170 | 9B0A60E024B7566000812410 = { 171 | CreatedOnToolsVersion = 11.5; 172 | }; 173 | 9BFC1181249270E800AECF6C = { 174 | CreatedOnToolsVersion = 11.5; 175 | }; 176 | }; 177 | }; 178 | buildConfigurationList = 9BFC117C249270E800AECF6C /* Build configuration list for PBXProject "SwiftGradients" */; 179 | compatibilityVersion = "Xcode 9.3"; 180 | developmentRegion = en; 181 | hasScannedForEncodings = 0; 182 | knownRegions = ( 183 | en, 184 | Base, 185 | ); 186 | mainGroup = 9BFC1178249270E800AECF6C; 187 | productRefGroup = 9BFC1183249270E800AECF6C /* Products */; 188 | projectDirPath = ""; 189 | projectRoot = ""; 190 | targets = ( 191 | 9BFC1181249270E800AECF6C /* SwiftGradients */, 192 | 9B0A60E024B7566000812410 /* SwiftGradientsTests */, 193 | ); 194 | }; 195 | /* End PBXProject section */ 196 | 197 | /* Begin PBXResourcesBuildPhase section */ 198 | 9B0A60DF24B7566000812410 /* Resources */ = { 199 | isa = PBXResourcesBuildPhase; 200 | buildActionMask = 2147483647; 201 | files = ( 202 | ); 203 | runOnlyForDeploymentPostprocessing = 0; 204 | }; 205 | 9BFC1180249270E800AECF6C /* Resources */ = { 206 | isa = PBXResourcesBuildPhase; 207 | buildActionMask = 2147483647; 208 | files = ( 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | }; 212 | /* End PBXResourcesBuildPhase section */ 213 | 214 | /* Begin PBXSourcesBuildPhase section */ 215 | 9B0A60DD24B7566000812410 /* Sources */ = { 216 | isa = PBXSourcesBuildPhase; 217 | buildActionMask = 2147483647; 218 | files = ( 219 | 9B0A60E424B7566000812410 /* SwiftGradientsTests.swift in Sources */, 220 | ); 221 | runOnlyForDeploymentPostprocessing = 0; 222 | }; 223 | 9BFC117E249270E800AECF6C /* Sources */ = { 224 | isa = PBXSourcesBuildPhase; 225 | buildActionMask = 2147483647; 226 | files = ( 227 | 9B0A60CE24B658D000812410 /* UIViewGradientsExtension.swift in Sources */, 228 | 9B0A60D224B65A6D00812410 /* CAGradientLayerExtension.swift in Sources */, 229 | 9B0A60D024B6595000812410 /* CALayerGradientsExtension.swift in Sources */, 230 | ); 231 | runOnlyForDeploymentPostprocessing = 0; 232 | }; 233 | /* End PBXSourcesBuildPhase section */ 234 | 235 | /* Begin PBXTargetDependency section */ 236 | 9B0A60E824B7566000812410 /* PBXTargetDependency */ = { 237 | isa = PBXTargetDependency; 238 | platformFilter = ios; 239 | target = 9BFC1181249270E800AECF6C /* SwiftGradients */; 240 | targetProxy = 9B0A60E724B7566000812410 /* PBXContainerItemProxy */; 241 | }; 242 | /* End PBXTargetDependency section */ 243 | 244 | /* Begin XCBuildConfiguration section */ 245 | 9B0A60EA24B7566000812410 /* Debug */ = { 246 | isa = XCBuildConfiguration; 247 | buildSettings = { 248 | CODE_SIGN_STYLE = Automatic; 249 | DEVELOPMENT_TEAM = WNU857N39T; 250 | INFOPLIST_FILE = SwiftGradientsTests/Info.plist; 251 | IPHONEOS_DEPLOYMENT_TARGET = 13.5; 252 | LD_RUNPATH_SEARCH_PATHS = ( 253 | "$(inherited)", 254 | "@executable_path/Frameworks", 255 | "@loader_path/Frameworks", 256 | ); 257 | ONLY_ACTIVE_ARCH = YES; 258 | PRODUCT_BUNDLE_IDENTIFIER = com.rootstrap.SwiftGradientsTests; 259 | PRODUCT_NAME = "$(TARGET_NAME)"; 260 | SWIFT_VERSION = 5.0; 261 | TARGETED_DEVICE_FAMILY = "1,2"; 262 | }; 263 | name = Debug; 264 | }; 265 | 9B0A60EB24B7566000812410 /* Release */ = { 266 | isa = XCBuildConfiguration; 267 | buildSettings = { 268 | CODE_SIGN_STYLE = Automatic; 269 | DEVELOPMENT_TEAM = WNU857N39T; 270 | INFOPLIST_FILE = SwiftGradientsTests/Info.plist; 271 | IPHONEOS_DEPLOYMENT_TARGET = 13.5; 272 | LD_RUNPATH_SEARCH_PATHS = ( 273 | "$(inherited)", 274 | "@executable_path/Frameworks", 275 | "@loader_path/Frameworks", 276 | ); 277 | PRODUCT_BUNDLE_IDENTIFIER = com.rootstrap.SwiftGradientsTests; 278 | PRODUCT_NAME = "$(TARGET_NAME)"; 279 | SWIFT_VERSION = 5.0; 280 | TARGETED_DEVICE_FAMILY = "1,2"; 281 | }; 282 | name = Release; 283 | }; 284 | 9BFC1188249270E800AECF6C /* Debug */ = { 285 | isa = XCBuildConfiguration; 286 | buildSettings = { 287 | ALWAYS_SEARCH_USER_PATHS = NO; 288 | CLANG_ANALYZER_NONNULL = YES; 289 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 290 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 291 | CLANG_CXX_LIBRARY = "libc++"; 292 | CLANG_ENABLE_MODULES = YES; 293 | CLANG_ENABLE_OBJC_ARC = YES; 294 | CLANG_ENABLE_OBJC_WEAK = YES; 295 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 296 | CLANG_WARN_BOOL_CONVERSION = YES; 297 | CLANG_WARN_COMMA = YES; 298 | CLANG_WARN_CONSTANT_CONVERSION = YES; 299 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 300 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 301 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 302 | CLANG_WARN_EMPTY_BODY = YES; 303 | CLANG_WARN_ENUM_CONVERSION = YES; 304 | CLANG_WARN_INFINITE_RECURSION = YES; 305 | CLANG_WARN_INT_CONVERSION = YES; 306 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 307 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 308 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 309 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 310 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 311 | CLANG_WARN_STRICT_PROTOTYPES = YES; 312 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 313 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 314 | CLANG_WARN_UNREACHABLE_CODE = YES; 315 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 316 | COPY_PHASE_STRIP = NO; 317 | CURRENT_PROJECT_VERSION = 1; 318 | DEBUG_INFORMATION_FORMAT = dwarf; 319 | ENABLE_STRICT_OBJC_MSGSEND = YES; 320 | ENABLE_TESTABILITY = YES; 321 | GCC_C_LANGUAGE_STANDARD = gnu11; 322 | GCC_DYNAMIC_NO_PIC = NO; 323 | GCC_NO_COMMON_BLOCKS = YES; 324 | GCC_OPTIMIZATION_LEVEL = 0; 325 | GCC_PREPROCESSOR_DEFINITIONS = ( 326 | "DEBUG=1", 327 | "$(inherited)", 328 | ); 329 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 330 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 331 | GCC_WARN_UNDECLARED_SELECTOR = YES; 332 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 333 | GCC_WARN_UNUSED_FUNCTION = YES; 334 | GCC_WARN_UNUSED_VARIABLE = YES; 335 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 336 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 337 | MTL_FAST_MATH = YES; 338 | ONLY_ACTIVE_ARCH = NO; 339 | SDKROOT = iphoneos; 340 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 341 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 342 | VERSIONING_SYSTEM = "apple-generic"; 343 | VERSION_INFO_PREFIX = ""; 344 | }; 345 | name = Debug; 346 | }; 347 | 9BFC1189249270E800AECF6C /* Release */ = { 348 | isa = XCBuildConfiguration; 349 | buildSettings = { 350 | ALWAYS_SEARCH_USER_PATHS = NO; 351 | CLANG_ANALYZER_NONNULL = YES; 352 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 353 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 354 | CLANG_CXX_LIBRARY = "libc++"; 355 | CLANG_ENABLE_MODULES = YES; 356 | CLANG_ENABLE_OBJC_ARC = YES; 357 | CLANG_ENABLE_OBJC_WEAK = YES; 358 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 359 | CLANG_WARN_BOOL_CONVERSION = YES; 360 | CLANG_WARN_COMMA = YES; 361 | CLANG_WARN_CONSTANT_CONVERSION = YES; 362 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 363 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 364 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 365 | CLANG_WARN_EMPTY_BODY = YES; 366 | CLANG_WARN_ENUM_CONVERSION = YES; 367 | CLANG_WARN_INFINITE_RECURSION = YES; 368 | CLANG_WARN_INT_CONVERSION = YES; 369 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 370 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 371 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 372 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 373 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 374 | CLANG_WARN_STRICT_PROTOTYPES = YES; 375 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 376 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 377 | CLANG_WARN_UNREACHABLE_CODE = YES; 378 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 379 | COPY_PHASE_STRIP = NO; 380 | CURRENT_PROJECT_VERSION = 1; 381 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 382 | ENABLE_NS_ASSERTIONS = NO; 383 | ENABLE_STRICT_OBJC_MSGSEND = YES; 384 | GCC_C_LANGUAGE_STANDARD = gnu11; 385 | GCC_NO_COMMON_BLOCKS = YES; 386 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 387 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 388 | GCC_WARN_UNDECLARED_SELECTOR = YES; 389 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 390 | GCC_WARN_UNUSED_FUNCTION = YES; 391 | GCC_WARN_UNUSED_VARIABLE = YES; 392 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 393 | MTL_ENABLE_DEBUG_INFO = NO; 394 | MTL_FAST_MATH = YES; 395 | SDKROOT = iphoneos; 396 | SWIFT_COMPILATION_MODE = wholemodule; 397 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 398 | VALIDATE_PRODUCT = YES; 399 | VERSIONING_SYSTEM = "apple-generic"; 400 | VERSION_INFO_PREFIX = ""; 401 | }; 402 | name = Release; 403 | }; 404 | 9BFC118B249270E800AECF6C /* Debug */ = { 405 | isa = XCBuildConfiguration; 406 | buildSettings = { 407 | CODE_SIGN_STYLE = Automatic; 408 | COMPANY_NAME = com.rootstrap; 409 | DEFINES_MODULE = YES; 410 | DEVELOPMENT_TEAM = WNU857N39T; 411 | DYLIB_COMPATIBILITY_VERSION = 1; 412 | DYLIB_CURRENT_VERSION = 1; 413 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 414 | INFOPLIST_FILE = "Supporting Files/Info.plist"; 415 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 416 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 417 | LD_RUNPATH_SEARCH_PATHS = ( 418 | "$(inherited)", 419 | "@executable_path/Frameworks", 420 | "@loader_path/Frameworks", 421 | ); 422 | LIB_NAME = SwiftGradients; 423 | MARKETING_VERSION = 1.0.0; 424 | PRODUCT_BUNDLE_IDENTIFIER = "$(COMPANY_NAME).$(LIB_NAME)"; 425 | PRODUCT_NAME = SwiftGradients; 426 | SKIP_INSTALL = YES; 427 | SUPPORTS_MACCATALYST = NO; 428 | SWIFT_VERSION = 5.0; 429 | TARGETED_DEVICE_FAMILY = "1,2"; 430 | }; 431 | name = Debug; 432 | }; 433 | 9BFC118C249270E800AECF6C /* Release */ = { 434 | isa = XCBuildConfiguration; 435 | buildSettings = { 436 | CODE_SIGN_STYLE = Automatic; 437 | COMPANY_NAME = com.rootstrap; 438 | DEFINES_MODULE = YES; 439 | DEVELOPMENT_TEAM = WNU857N39T; 440 | DYLIB_COMPATIBILITY_VERSION = 1; 441 | DYLIB_CURRENT_VERSION = 1; 442 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 443 | INFOPLIST_FILE = "Supporting Files/Info.plist"; 444 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 445 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 446 | LD_RUNPATH_SEARCH_PATHS = ( 447 | "$(inherited)", 448 | "@executable_path/Frameworks", 449 | "@loader_path/Frameworks", 450 | ); 451 | LIB_NAME = SwiftGradients; 452 | MARKETING_VERSION = 1.0.0; 453 | PRODUCT_BUNDLE_IDENTIFIER = "$(COMPANY_NAME).$(LIB_NAME)"; 454 | PRODUCT_NAME = SwiftGradients; 455 | SKIP_INSTALL = YES; 456 | SUPPORTS_MACCATALYST = NO; 457 | SWIFT_VERSION = 5.0; 458 | TARGETED_DEVICE_FAMILY = "1,2"; 459 | }; 460 | name = Release; 461 | }; 462 | /* End XCBuildConfiguration section */ 463 | 464 | /* Begin XCConfigurationList section */ 465 | 9B0A60E924B7566000812410 /* Build configuration list for PBXNativeTarget "SwiftGradientsTests" */ = { 466 | isa = XCConfigurationList; 467 | buildConfigurations = ( 468 | 9B0A60EA24B7566000812410 /* Debug */, 469 | 9B0A60EB24B7566000812410 /* Release */, 470 | ); 471 | defaultConfigurationIsVisible = 0; 472 | defaultConfigurationName = Release; 473 | }; 474 | 9BFC117C249270E800AECF6C /* Build configuration list for PBXProject "SwiftGradients" */ = { 475 | isa = XCConfigurationList; 476 | buildConfigurations = ( 477 | 9BFC1188249270E800AECF6C /* Debug */, 478 | 9BFC1189249270E800AECF6C /* Release */, 479 | ); 480 | defaultConfigurationIsVisible = 0; 481 | defaultConfigurationName = Release; 482 | }; 483 | 9BFC118A249270E800AECF6C /* Build configuration list for PBXNativeTarget "SwiftGradients" */ = { 484 | isa = XCConfigurationList; 485 | buildConfigurations = ( 486 | 9BFC118B249270E800AECF6C /* Debug */, 487 | 9BFC118C249270E800AECF6C /* Release */, 488 | ); 489 | defaultConfigurationIsVisible = 0; 490 | defaultConfigurationName = Release; 491 | }; 492 | /* End XCConfigurationList section */ 493 | }; 494 | rootObject = 9BFC1179249270E800AECF6C /* Project object */; 495 | } 496 | -------------------------------------------------------------------------------- /SwiftGradients.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftGradients.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftGradients.xcodeproj/xcshareddata/xcschemes/SwiftGradients.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 57 | 58 | 59 | 60 | 62 | 63 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /SwiftGradients.xcodeproj/xcshareddata/xcschemes/SwiftGradientsTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 37 | 38 | 44 | 45 | 47 | 48 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /SwiftGradientsTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /SwiftGradientsTests/SwiftGradientsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftGradientsTests.swift 3 | // SwiftGradientsTests 4 | // 5 | // Created by German on 7/9/20. 6 | // Copyright © 2020 Rootstrap. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import UIKit 11 | import SwiftGradients 12 | 13 | class SwiftGradientsTests: XCTestCase { 14 | 15 | var view: UIView! 16 | 17 | override func setUpWithError() throws { 18 | view = UIView() 19 | } 20 | 21 | func testGradientLayerIsAdded() { 22 | let gradientLayer = view.addGradient( 23 | colors: [.white, .black], 24 | direction: .topToBottom 25 | ) 26 | let firstSublayer = view.layer.sublayers?.first 27 | XCTAssert(firstSublayer is CAGradientLayer) 28 | XCTAssert(gradientLayer === firstSublayer) 29 | } 30 | 31 | func testGradientCorrectValues() { 32 | let locations = [50, 70] 33 | let colors: [UIColor] = [.blue, .red] 34 | let gradientLayer = view.addGradient( 35 | colors: colors, 36 | direction: .leftToRight, 37 | locations: locations 38 | ) 39 | 40 | guard let gradientColors = gradientLayer.colors else { 41 | XCTFail("Gradient colors are missing.") 42 | return 43 | } 44 | XCTAssert(gradientColors.count == colors.count) 45 | for (index, gradientColor) in gradientColors.enumerated() { 46 | guard CFGetTypeID(gradientColor as CFTypeRef) == CGColor.typeID else { 47 | XCTFail("CAGradientLayer color is not a CGColor.") 48 | return 49 | } 50 | XCTAssert(gradientColor as! CGColor == colors[index].cgColor) 51 | } 52 | 53 | XCTAssert(gradientLayer.startPoint == CGPoint(x: 0, y: 0.5)) 54 | XCTAssert(gradientLayer.endPoint == CGPoint(x: 1, y: 0.5)) 55 | 56 | guard let colorLocations = gradientLayer.locations else { 57 | XCTFail("Gradient colors locations are missing.") 58 | return 59 | } 60 | for (index, colorLocation) in colorLocations.enumerated() { 61 | XCTAssert(colorLocation == NSNumber(value: Float(locations[index]) / 100)) 62 | } 63 | } 64 | 65 | func testGradientAngleCalculation() { 66 | let colors: [UIColor] = [.yellow, .green] 67 | var gradientLayer = view.addGradient(colors: colors, angle: 90) 68 | XCTAssert(gradientLayer.startPoint == CGPoint(x: 0.5, y: 0)) 69 | XCTAssert(gradientLayer.endPoint == CGPoint(x: 0.5, y: 1)) 70 | 71 | gradientLayer = view.addGradient(colors: colors, angle: 270) 72 | XCTAssert(gradientLayer.startPoint == CGPoint(x: 0.5, y: 1)) 73 | XCTAssert(gradientLayer.endPoint == CGPoint(x: 0.5, y: 0)) 74 | 75 | gradientLayer = view.addGradient(colors: colors, angle: 180) 76 | XCTAssert(gradientLayer.startPoint == CGPoint(x: 0, y: 0.5)) 77 | XCTAssert(gradientLayer.endPoint == CGPoint(x: 1, y: 0.5)) 78 | 79 | gradientLayer = view.addGradient(colors: colors, angle: 0) 80 | XCTAssert(gradientLayer.startPoint == CGPoint(x: 1, y: 0.5)) 81 | XCTAssert(gradientLayer.endPoint == CGPoint(x: 0, y: 0.5)) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /gradient-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/SwiftGradients/21d2fc679dd928d040980ef4c495c8e681ceb7eb/gradient-preview.png --------------------------------------------------------------------------------