commit 8fe54e2dcc1de21dbc41f0316c18975c4f1f6c8d Author: Felix Mau Date: Wed Dec 28 11:42:55 2016 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cab8c69 --- /dev/null +++ b/.gitignore @@ -0,0 +1,65 @@ +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## Build generated +build/ +DerivedData/ + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ + +## Other +*.moved-aside +*.xcuserstate + +## Obj-C/Swift specific +*.hmap +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +.build/ + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# Pods/ + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output \ No newline at end of file diff --git a/GradientLoadingBar.podspec b/GradientLoadingBar.podspec new file mode 100644 index 0000000..d1c6802 --- /dev/null +++ b/GradientLoadingBar.podspec @@ -0,0 +1,15 @@ +Pod::Spec.new do |s| + s.name = 'GradientLoadingBar' + s.version = '1.0.0' + s.license = 'MIT' + s.summary = 'An animated gradient loading bar.' + s.description = 'An animated gradient loading bar. Inspired by http://www.cssscript.com/ios-style-gradient-progress-bar-with-pure-css-css3/' + s.homepage = 'http://felix.hamburg' + s.author = { 'Felix Mau' => 'contact@felix.hamburg' } + + s.source = { :git => 'https://github.com/fxm90/GradientLoadingBar', :tag => '1.0.0' } + s.source_files = 'GradientLoadingBar', 'GradientLoadingBar/**/*.swift' + + s.platform = :ios, '9.0' + s.pod_target_xcconfig = { 'SWIFT_VERSION' => '3' } +end \ No newline at end of file diff --git a/GradientLoadingBar.xcodeproj/project.pbxproj b/GradientLoadingBar.xcodeproj/project.pbxproj new file mode 100644 index 0000000..de19778 --- /dev/null +++ b/GradientLoadingBar.xcodeproj/project.pbxproj @@ -0,0 +1,465 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 97AE29101E13C5B400C454C6 /* GradientLoadingBar.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 97AE29061E13C5B400C454C6 /* GradientLoadingBar.framework */; }; + 97AE29151E13C5B400C454C6 /* GradientLoadingBarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97AE29141E13C5B400C454C6 /* GradientLoadingBarTests.swift */; }; + 97AE29171E13C5B400C454C6 /* GradientLoadingBar.h in Headers */ = {isa = PBXBuildFile; fileRef = 97AE29091E13C5B400C454C6 /* GradientLoadingBar.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 97AE29281E13C5E500C454C6 /* GradientLoadingBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97AE29201E13C5E500C454C6 /* GradientLoadingBar.swift */; }; + 97AE29291E13C5E500C454C6 /* UIColor+Initializers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97AE29231E13C5E500C454C6 /* UIColor+Initializers.swift */; }; + 97AE292A1E13C5E500C454C6 /* Durations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97AE29251E13C5E500C454C6 /* Durations.swift */; }; + 97AE292B1E13C5E500C454C6 /* GradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97AE29271E13C5E500C454C6 /* GradientView.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 97AE29111E13C5B400C454C6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97AE28FD1E13C5B400C454C6 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97AE29051E13C5B400C454C6; + remoteInfo = GradientLoadingBar; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 97AE29061E13C5B400C454C6 /* GradientLoadingBar.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GradientLoadingBar.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 97AE29091E13C5B400C454C6 /* GradientLoadingBar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GradientLoadingBar.h; sourceTree = ""; }; + 97AE290A1E13C5B400C454C6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 97AE290F1E13C5B400C454C6 /* GradientLoadingBarTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GradientLoadingBarTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 97AE29141E13C5B400C454C6 /* GradientLoadingBarTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GradientLoadingBarTests.swift; sourceTree = ""; }; + 97AE29161E13C5B400C454C6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 97AE29201E13C5E500C454C6 /* GradientLoadingBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GradientLoadingBar.swift; sourceTree = ""; }; + 97AE29231E13C5E500C454C6 /* UIColor+Initializers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Initializers.swift"; sourceTree = ""; }; + 97AE29251E13C5E500C454C6 /* Durations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Durations.swift; sourceTree = ""; }; + 97AE29271E13C5E500C454C6 /* GradientView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GradientView.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97AE29021E13C5B400C454C6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97AE290C1E13C5B400C454C6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 97AE29101E13C5B400C454C6 /* GradientLoadingBar.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 97AE28FC1E13C5B400C454C6 = { + isa = PBXGroup; + children = ( + 97AE29081E13C5B400C454C6 /* GradientLoadingBar */, + 97AE29131E13C5B400C454C6 /* GradientLoadingBarTests */, + 97AE29071E13C5B400C454C6 /* Products */, + ); + sourceTree = ""; + }; + 97AE29071E13C5B400C454C6 /* Products */ = { + isa = PBXGroup; + children = ( + 97AE29061E13C5B400C454C6 /* GradientLoadingBar.framework */, + 97AE290F1E13C5B400C454C6 /* GradientLoadingBarTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97AE29081E13C5B400C454C6 /* GradientLoadingBar */ = { + isa = PBXGroup; + children = ( + 97AE29211E13C5E500C454C6 /* Helpers */, + 97AE29261E13C5E500C454C6 /* Views */, + 97AE29201E13C5E500C454C6 /* GradientLoadingBar.swift */, + 97AE29091E13C5B400C454C6 /* GradientLoadingBar.h */, + 97AE290A1E13C5B400C454C6 /* Info.plist */, + ); + path = GradientLoadingBar; + sourceTree = ""; + }; + 97AE29131E13C5B400C454C6 /* GradientLoadingBarTests */ = { + isa = PBXGroup; + children = ( + 97AE29141E13C5B400C454C6 /* GradientLoadingBarTests.swift */, + 97AE29161E13C5B400C454C6 /* Info.plist */, + ); + path = GradientLoadingBarTests; + sourceTree = ""; + }; + 97AE29211E13C5E500C454C6 /* Helpers */ = { + isa = PBXGroup; + children = ( + 97AE29221E13C5E500C454C6 /* Extensions */, + 97AE29241E13C5E500C454C6 /* Structs */, + ); + path = Helpers; + sourceTree = ""; + }; + 97AE29221E13C5E500C454C6 /* Extensions */ = { + isa = PBXGroup; + children = ( + 97AE29231E13C5E500C454C6 /* UIColor+Initializers.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + 97AE29241E13C5E500C454C6 /* Structs */ = { + isa = PBXGroup; + children = ( + 97AE29251E13C5E500C454C6 /* Durations.swift */, + ); + path = Structs; + sourceTree = ""; + }; + 97AE29261E13C5E500C454C6 /* Views */ = { + isa = PBXGroup; + children = ( + 97AE29271E13C5E500C454C6 /* GradientView.swift */, + ); + path = Views; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 97AE29031E13C5B400C454C6 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 97AE29171E13C5B400C454C6 /* GradientLoadingBar.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 97AE29051E13C5B400C454C6 /* GradientLoadingBar */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97AE291A1E13C5B400C454C6 /* Build configuration list for PBXNativeTarget "GradientLoadingBar" */; + buildPhases = ( + 97AE29011E13C5B400C454C6 /* Sources */, + 97AE29021E13C5B400C454C6 /* Frameworks */, + 97AE29031E13C5B400C454C6 /* Headers */, + 97AE29041E13C5B400C454C6 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = GradientLoadingBar; + productName = GradientLoadingBar; + productReference = 97AE29061E13C5B400C454C6 /* GradientLoadingBar.framework */; + productType = "com.apple.product-type.framework"; + }; + 97AE290E1E13C5B400C454C6 /* GradientLoadingBarTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97AE291D1E13C5B400C454C6 /* Build configuration list for PBXNativeTarget "GradientLoadingBarTests" */; + buildPhases = ( + 97AE290B1E13C5B400C454C6 /* Sources */, + 97AE290C1E13C5B400C454C6 /* Frameworks */, + 97AE290D1E13C5B400C454C6 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 97AE29121E13C5B400C454C6 /* PBXTargetDependency */, + ); + name = GradientLoadingBarTests; + productName = GradientLoadingBarTests; + productReference = 97AE290F1E13C5B400C454C6 /* GradientLoadingBarTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97AE28FD1E13C5B400C454C6 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0810; + LastUpgradeCheck = 0810; + ORGANIZATIONNAME = "Felix Mau"; + TargetAttributes = { + 97AE29051E13C5B400C454C6 = { + CreatedOnToolsVersion = 8.1; + LastSwiftMigration = 0810; + ProvisioningStyle = Automatic; + }; + 97AE290E1E13C5B400C454C6 = { + CreatedOnToolsVersion = 8.1; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 97AE29001E13C5B400C454C6 /* Build configuration list for PBXProject "GradientLoadingBar" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 97AE28FC1E13C5B400C454C6; + productRefGroup = 97AE29071E13C5B400C454C6 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97AE29051E13C5B400C454C6 /* GradientLoadingBar */, + 97AE290E1E13C5B400C454C6 /* GradientLoadingBarTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97AE29041E13C5B400C454C6 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97AE290D1E13C5B400C454C6 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97AE29011E13C5B400C454C6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97AE29291E13C5E500C454C6 /* UIColor+Initializers.swift in Sources */, + 97AE29281E13C5E500C454C6 /* GradientLoadingBar.swift in Sources */, + 97AE292B1E13C5E500C454C6 /* GradientView.swift in Sources */, + 97AE292A1E13C5E500C454C6 /* Durations.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97AE290B1E13C5B400C454C6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97AE29151E13C5B400C454C6 /* GradientLoadingBarTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 97AE29121E13C5B400C454C6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97AE29051E13C5B400C454C6 /* GradientLoadingBar */; + targetProxy = 97AE29111E13C5B400C454C6 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 97AE29181E13C5B400C454C6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.1; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 97AE29191E13C5B400C454C6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.1; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 97AE291B1E13C5B400C454C6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = GradientLoadingBar/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = hamburg.felix.GradientLoadingBar; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; + }; + name = Debug; + }; + 97AE291C1E13C5B400C454C6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = GradientLoadingBar/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = hamburg.felix.GradientLoadingBar; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; + }; + name = Release; + }; + 97AE291E1E13C5B400C454C6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + INFOPLIST_FILE = GradientLoadingBarTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = hamburg.felix.GradientLoadingBarTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Debug; + }; + 97AE291F1E13C5B400C454C6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + INFOPLIST_FILE = GradientLoadingBarTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = hamburg.felix.GradientLoadingBarTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97AE29001E13C5B400C454C6 /* Build configuration list for PBXProject "GradientLoadingBar" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97AE29181E13C5B400C454C6 /* Debug */, + 97AE29191E13C5B400C454C6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97AE291A1E13C5B400C454C6 /* Build configuration list for PBXNativeTarget "GradientLoadingBar" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97AE291B1E13C5B400C454C6 /* Debug */, + 97AE291C1E13C5B400C454C6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; + 97AE291D1E13C5B400C454C6 /* Build configuration list for PBXNativeTarget "GradientLoadingBarTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97AE291E1E13C5B400C454C6 /* Debug */, + 97AE291F1E13C5B400C454C6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97AE28FD1E13C5B400C454C6 /* Project object */; +} diff --git a/GradientLoadingBar.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/GradientLoadingBar.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..9d8a6fe --- /dev/null +++ b/GradientLoadingBar.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/GradientLoadingBar/GradientLoadingBar.h b/GradientLoadingBar/GradientLoadingBar.h new file mode 100644 index 0000000..4d3494c --- /dev/null +++ b/GradientLoadingBar/GradientLoadingBar.h @@ -0,0 +1,19 @@ +// +// GradientLoadingBar.h +// GradientLoadingBar +// +// Created by Felix Mau on 28.12.16. +// Copyright © 2016 Felix Mau. All rights reserved. +// + +#import + +//! Project version number for GradientLoadingBar. +FOUNDATION_EXPORT double GradientLoadingBarVersionNumber; + +//! Project version string for GradientLoadingBar. +FOUNDATION_EXPORT const unsigned char GradientLoadingBarVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/GradientLoadingBar/GradientLoadingBar.swift b/GradientLoadingBar/GradientLoadingBar.swift new file mode 100644 index 0000000..a199b5e --- /dev/null +++ b/GradientLoadingBar/GradientLoadingBar.swift @@ -0,0 +1,101 @@ +// +// GradientLoadingBar.swift +// GradientLoadingBar +// +// Created by Felix Mau on 11.12.16. +// Copyright © 2016 Felix Mau. All rights reserved. +// + +import Foundation +import UIKit + +// Default configuration +let defaults = ( + height: 2.5, + durations: Durations(fadeIn: 0.33, fadeOut: 0.66, progress: 3.33) +) + +public class GradientLoadingBar { + private let height : Double + private let durations : Durations! + + private lazy var gradientView : GradientView! = self.initGradientView() + private var gradientViewAddedToWindow : Bool = false + + private var visibilityCounter : Int + + private static var instance : GradientLoadingBar? + + public init ( + height: Double = defaults.height, + durations: Durations! = defaults.durations + ) { + self.height = height + self.durations = durations + + self.visibilityCounter = 0 // Used to handle mutliple calls to show/hide at the same time + } + + deinit { + if (self.gradientViewAddedToWindow) { + self.gradientView.removeFromSuperview() + } + } + + // MARK: Lazy Initialization + + func initGradientView() -> GradientView { + let window = UIApplication.shared.keyWindow! + let frame = window.frame + + let gradientView = GradientView( + frame: CGRect(x: frame.origin.x, y: frame.origin.y, width: frame.size.width, height: CGFloat(self.height)), + durations: durations + ) + + // Add view to main window + window.addSubview(gradientView) + self.gradientViewAddedToWindow = true + + // Layout + gradientView.leadingAnchor.constraint(equalTo: window.leadingAnchor).isActive = true + gradientView.trailingAnchor.constraint(equalTo: window.trailingAnchor).isActive = true + gradientView.topAnchor.constraint(equalTo: window.topAnchor).isActive = true + + return gradientView + } + + + // MARK: Helper to use as a Singleton + + public func saveInstance() { + type(of: self).instance = self; + } + + public static func sharedInstance() -> GradientLoadingBar! { + if (instance == nil) { + instance = GradientLoadingBar() + } + + return instance! + } + + + // MARK: Show / Hide + + public func show() { + if (self.visibilityCounter == 0) { + self.gradientView.show() + } + + self.visibilityCounter += 1 + } + + public func hide() { + self.visibilityCounter -= 1 + + if (self.visibilityCounter == 0) { + self.gradientView.hide() + } + } +} diff --git a/GradientLoadingBar/Helpers/Extensions/UIColor+Initializers.swift b/GradientLoadingBar/Helpers/Extensions/UIColor+Initializers.swift new file mode 100644 index 0000000..f3fbd69 --- /dev/null +++ b/GradientLoadingBar/Helpers/Extensions/UIColor+Initializers.swift @@ -0,0 +1,64 @@ +// +// UIColor+AbsoluteRGB.swift +// GradientLoadingBar +// +// Created by Felix Mau on 20.11.16. +// Copyright © 2016 Felix Mau. All rights reserved. +// + +import Foundation +import UIKit + +extension UIColor { + // Create color from RGB + convenience init(absoluteRed: Int, green: Int, blue: Int) { + self.init( + absoluteRed: absoluteRed, + green: green, + blue: blue, + alpha: 1.0 + ) + } + + // Create color from RGBA + convenience init(absoluteRed: Int, green: Int, blue: Int, alpha: CGFloat) { + let normalizedRed = CGFloat(absoluteRed) / 255 + let normalizedGreen = CGFloat(green) / 255 + let normalizedBlue = CGFloat(blue) / 255 + + self.init( + red: normalizedRed, + green: normalizedGreen, + blue: normalizedBlue, + alpha: alpha + ) + } + + // Color from HEX-Value + // Based on: http://stackoverflow.com/a/24263296 + convenience init(hexValue:Int) { + self.init( + absoluteRed: (hexValue >> 16) & 0xff, + green: (hexValue >> 8) & 0xff, + blue: hexValue & 0xff + ) + } + + // Color from HEX-String + // Based on: http://stackoverflow.com/a/27203691 + convenience init(hexString:String) { + var normalizedHexString = hexString.trimmingCharacters(in: .whitespacesAndNewlines).uppercased() + + if (normalizedHexString.hasPrefix("#")) { + normalizedHexString.remove(at: normalizedHexString.startIndex) + } + + // Convert to hexadecimal integer + var hexValue:UInt32 = 0 + Scanner(string: normalizedHexString).scanHexInt32(&hexValue) + + self.init( + hexValue:Int(hexValue) + ) + } +} diff --git a/GradientLoadingBar/Helpers/Structs/Durations.swift b/GradientLoadingBar/Helpers/Structs/Durations.swift new file mode 100644 index 0000000..f5c4997 --- /dev/null +++ b/GradientLoadingBar/Helpers/Structs/Durations.swift @@ -0,0 +1,22 @@ +// +// Durations.swift +// GradientLoadingBar +// +// Created by Felix Mau on 27.12.16. +// Copyright © 2016 Felix Mau. All rights reserved. +// + +import Foundation + +public struct Durations { + var fadeIn : Double = 0.0 + var fadeOut : Double = 0.0 + var progress : Double = 0.0 + + // Self-written initializer is required to allow public access (http://stackoverflow.com/a/26224873/3532505) + public init(fadeIn: Double = 0.0, fadeOut: Double = 0.0, progress: Double = 0.0) { + self.fadeIn = fadeIn; + self.fadeOut = fadeOut; + self.progress = progress; + } +} diff --git a/GradientLoadingBar/Info.plist b/GradientLoadingBar/Info.plist new file mode 100644 index 0000000..fbe1e6b --- /dev/null +++ b/GradientLoadingBar/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/GradientLoadingBar/Views/GradientView.swift b/GradientLoadingBar/Views/GradientView.swift new file mode 100644 index 0000000..a3818c7 --- /dev/null +++ b/GradientLoadingBar/Views/GradientView.swift @@ -0,0 +1,121 @@ +// +// GradientView.swift +// GradientLoadingBar +// +// Created by Felix Mau on 10.12.16. +// Copyright © 2016 Felix Mau. All rights reserved. +// + +import Foundation +import UIKit + +class GradientView : UIView, CAAnimationDelegate { + private let config = ( + fadeInAnimationKey: "GradientView--fade-in", + fadeOutAnimationKey: "GradientView--fade-out", + progressAnimationKey: "GradientView--progress" + ) + + private var gradientLayer : CAGradientLayer! + + private var durations : Durations + + override init (frame : CGRect) { + self.gradientLayer = CAGradientLayer() + self.gradientLayer.frame = CGRect(x: 0, y: 0, width: 3 * frame.size.width, height: frame.size.height) + + self.gradientLayer.anchorPoint = CGPoint(x: 0, y: 0) + self.gradientLayer.position = CGPoint(x: -2 * frame.size.width, y: 0) + + self.gradientLayer.opacity = 0.0 + + self.gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0); + self.gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.0); + + // Colors from http://www.cssscript.com/ios-style-gradient-progress-bar-with-pure-css-css3/ + let gradientColors = [ + UIColor(hexString:"#4cd964").cgColor, + UIColor(hexString:"#5ac8fa").cgColor, + UIColor(hexString:"#007aff").cgColor, + UIColor(hexString:"#34aadc").cgColor, + UIColor(hexString:"#5856d6").cgColor, + UIColor(hexString:"#ff2d55").cgColor + ] + + // Append reversed gradient to simulate infinte animation + self.gradientLayer.colors = gradientColors + gradientColors.reversed() + gradientColors; + + // Set default durations to 0 + self.durations = Durations() + + // Add layer to view + super.init(frame : frame) + self.layer.insertSublayer(gradientLayer, at: 0) + } + + convenience init(frame: CGRect, durations : Durations) { + self.init(frame: frame) + self.durations = durations + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Progress animations (automatically triggered via delegates) + + func animationDidStart(_ anim: CAAnimation) { + if (anim == self.gradientLayer.animation(forKey: config.fadeInAnimationKey)) { + // Start progress animation + let animation = CABasicAnimation(keyPath: "position") + animation.fromValue = CGPoint(x: -2 * self.frame.size.width, y: 0) + animation.toValue = CGPoint(x: 0, y: 0) + animation.duration = durations.progress + animation.repeatCount = Float.infinity + + self.gradientLayer.add(animation, forKey: config.progressAnimationKey) + } + } + + func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { + if (anim == self.gradientLayer.animation(forKey: config.fadeOutAnimationKey)) { + // Stop progress animation + self.gradientLayer.removeAnimation(forKey: self.config.progressAnimationKey) + } + } + + // MARK: Fade-In / Out + + func toggleGradientLayerVisibility(duration: TimeInterval, from: CGFloat, to: CGFloat, key: String) { + let animation = CABasicAnimation(keyPath: "opacity") + animation.delegate = self + + animation.duration = duration + animation.fromValue = from + animation.toValue = to + animation.fillMode = "forwards" + animation.isRemovedOnCompletion = false + + self.gradientLayer.add(animation, forKey: key) + } + + func show() { + self.gradientLayer.removeAnimation(forKey: config.fadeOutAnimationKey) + self.toggleGradientLayerVisibility( + duration: durations.fadeIn, + from: 0.0, + to: 1.0, + key: config.fadeInAnimationKey + ) + } + + func hide() { + self.gradientLayer.removeAnimation(forKey: config.fadeInAnimationKey) + self.toggleGradientLayerVisibility( + duration: durations.fadeOut, + from: 1.0, + to: 0.0, + key: config.fadeOutAnimationKey + ) + } +} diff --git a/GradientLoadingBarTests/GradientLoadingBarTests.swift b/GradientLoadingBarTests/GradientLoadingBarTests.swift new file mode 100644 index 0000000..05aff93 --- /dev/null +++ b/GradientLoadingBarTests/GradientLoadingBarTests.swift @@ -0,0 +1,36 @@ +// +// GradientLoadingBarTests.swift +// GradientLoadingBarTests +// +// Created by Felix Mau on 28.12.16. +// Copyright © 2016 Felix Mau. All rights reserved. +// + +import XCTest +@testable import GradientLoadingBar + +class GradientLoadingBarTests: XCTestCase { + + override func setUp() { + super.setUp() + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/GradientLoadingBarTests/Info.plist b/GradientLoadingBarTests/Info.plist new file mode 100644 index 0000000..6c6c23c --- /dev/null +++ b/GradientLoadingBarTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..8bacefc --- /dev/null +++ b/readme.md @@ -0,0 +1,50 @@ +GradientLoadingBar +==================== + +![Swift3.0](https://img.shields.io/badge/Swift-3.0-blue.svg?style=flat) +An animated gradient loading bar. +Inspired by [iOS Style Gradient Progress Bar with Pure CSS/CSS3](http://www.cssscript.com/ios-style-gradient-progress-bar-with-pure-css-css3/). + +![Example](http://felix.hamburg/files/github/gradient-loading-bar/screen.gif) + +### Integration +GradientLoadingBar can be added to your project using [CocoaPods](https://cocoapods.org/) by adding the following line to your Podfile: +``` +pod 'GradientLoadingBar', '~> 1.0' +``` +### How to use +First you need to import __"GradientLoadingBar"__. After that you can use __"GradientLoadingBar.sharedInstance()"__ to retrieve an instance to the loading bar. To show it, simply call the __"show()"__ method and after you're done call __"hide()"__. +``` +// Show loading bar +GradientLoadingBar.sharedInstance().show() + +// Do e.g. server calls etc. + +// Hide loading bar +GradientLoadingBar.sharedInstance().hide() +``` + +### Configuration +You can overwrite the default configuration by calling the initializers with the optional params __"height"__ and __"durations"__: +``` +let loadingBar = GradientLoadingBar( + height: 1.0, + durations: Durations(fadeIn: 1.0, fadeOut: 2.0, progress: 3.00) +) +``` + +If you don't want to save the instance on a variable and use the singleton instead, you can use the __"saveInstance()"__ method. Add the following code to your app delegate __"didFinishLaunchingWithOptions"__ method: +``` +GradientLoadingBar( + height: 1.0, + durations: Durations(fadeIn: 1.0, fadeOut: 2.0, progress: 3.00) +).saveInstance() +``` + +After that you can use __"GradientLoadingBar.sharedInstance()"__ as mentioned above. + +### Version +1.0.0 + +### Author +Felix Mau (contact(@)felix.hamburg)