├── .gitignore ├── .swiftpm └── xcode │ └── package.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── LICENSE ├── LoadingViewExample ├── LoadingViewExample.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── LoadingViewExample │ ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── ContentView.swift │ ├── LoadingViewExampleApp.swift │ └── Preview Content │ └── Preview Assets.xcassets │ └── Contents.json ├── Package.swift ├── README.md ├── Sources └── LoadingButton │ ├── CircleLoadingBar.swift │ ├── LoadingButton.swift │ └── LoadingButtonStyle.swift └── imgs ├── Example-1-1.png ├── Example-1.png ├── Example-2-1.png ├── Example-2.png ├── Logo.gif ├── Preview-zoom.gif ├── Preview.gif ├── Screenshot-Loading.png └── Screenshot.png /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /.swiftpm 4 | /Packages 5 | /*.xcodeproj 6 | xcuserdata/ 7 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Byun Kyung Min 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LoadingViewExample/LoadingViewExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 55; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1623BA1C275A05B100CCB3AE /* LoadingViewExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1623BA1B275A05B100CCB3AE /* LoadingViewExampleApp.swift */; }; 11 | 1623BA1E275A05B100CCB3AE /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1623BA1D275A05B100CCB3AE /* ContentView.swift */; }; 12 | 1623BA20275A05B300CCB3AE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1623BA1F275A05B300CCB3AE /* Assets.xcassets */; }; 13 | 1623BA23275A05B300CCB3AE /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1623BA22275A05B300CCB3AE /* Preview Assets.xcassets */; }; 14 | 16E766BE275A07A000E879D7 /* LoadingButton in Frameworks */ = {isa = PBXBuildFile; productRef = 16E766BD275A07A000E879D7 /* LoadingButton */; }; 15 | /* End PBXBuildFile section */ 16 | 17 | /* Begin PBXFileReference section */ 18 | 1623BA18275A05B100CCB3AE /* LoadingViewExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LoadingViewExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 19 | 1623BA1B275A05B100CCB3AE /* LoadingViewExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingViewExampleApp.swift; sourceTree = ""; }; 20 | 1623BA1D275A05B100CCB3AE /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 21 | 1623BA1F275A05B300CCB3AE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 22 | 1623BA22275A05B300CCB3AE /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 23 | 16E766BB275A076500E879D7 /* LoadingButton */ = {isa = PBXFileReference; lastKnownFileType = folder; name = LoadingButton; path = ..; sourceTree = ""; }; 24 | /* End PBXFileReference section */ 25 | 26 | /* Begin PBXFrameworksBuildPhase section */ 27 | 1623BA15275A05B100CCB3AE /* Frameworks */ = { 28 | isa = PBXFrameworksBuildPhase; 29 | buildActionMask = 2147483647; 30 | files = ( 31 | 16E766BE275A07A000E879D7 /* LoadingButton in Frameworks */, 32 | ); 33 | runOnlyForDeploymentPostprocessing = 0; 34 | }; 35 | /* End PBXFrameworksBuildPhase section */ 36 | 37 | /* Begin PBXGroup section */ 38 | 1623BA0F275A05B100CCB3AE = { 39 | isa = PBXGroup; 40 | children = ( 41 | 16E766BA275A076500E879D7 /* Packages */, 42 | 1623BA1A275A05B100CCB3AE /* LoadingViewExample */, 43 | 1623BA19275A05B100CCB3AE /* Products */, 44 | 16E766BC275A07A000E879D7 /* Frameworks */, 45 | ); 46 | sourceTree = ""; 47 | }; 48 | 1623BA19275A05B100CCB3AE /* Products */ = { 49 | isa = PBXGroup; 50 | children = ( 51 | 1623BA18275A05B100CCB3AE /* LoadingViewExample.app */, 52 | ); 53 | name = Products; 54 | sourceTree = ""; 55 | }; 56 | 1623BA1A275A05B100CCB3AE /* LoadingViewExample */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | 1623BA1B275A05B100CCB3AE /* LoadingViewExampleApp.swift */, 60 | 1623BA1D275A05B100CCB3AE /* ContentView.swift */, 61 | 1623BA1F275A05B300CCB3AE /* Assets.xcassets */, 62 | 1623BA21275A05B300CCB3AE /* Preview Content */, 63 | ); 64 | path = LoadingViewExample; 65 | sourceTree = ""; 66 | }; 67 | 1623BA21275A05B300CCB3AE /* Preview Content */ = { 68 | isa = PBXGroup; 69 | children = ( 70 | 1623BA22275A05B300CCB3AE /* Preview Assets.xcassets */, 71 | ); 72 | path = "Preview Content"; 73 | sourceTree = ""; 74 | }; 75 | 16E766BA275A076500E879D7 /* Packages */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | 16E766BB275A076500E879D7 /* LoadingButton */, 79 | ); 80 | name = Packages; 81 | sourceTree = ""; 82 | }; 83 | 16E766BC275A07A000E879D7 /* Frameworks */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | ); 87 | name = Frameworks; 88 | sourceTree = ""; 89 | }; 90 | /* End PBXGroup section */ 91 | 92 | /* Begin PBXNativeTarget section */ 93 | 1623BA17275A05B100CCB3AE /* LoadingViewExample */ = { 94 | isa = PBXNativeTarget; 95 | buildConfigurationList = 1623BA26275A05B300CCB3AE /* Build configuration list for PBXNativeTarget "LoadingViewExample" */; 96 | buildPhases = ( 97 | 1623BA14275A05B100CCB3AE /* Sources */, 98 | 1623BA15275A05B100CCB3AE /* Frameworks */, 99 | 1623BA16275A05B100CCB3AE /* Resources */, 100 | ); 101 | buildRules = ( 102 | ); 103 | dependencies = ( 104 | ); 105 | name = LoadingViewExample; 106 | packageProductDependencies = ( 107 | 16E766BD275A07A000E879D7 /* LoadingButton */, 108 | ); 109 | productName = LoadingViewExample; 110 | productReference = 1623BA18275A05B100CCB3AE /* LoadingViewExample.app */; 111 | productType = "com.apple.product-type.application"; 112 | }; 113 | /* End PBXNativeTarget section */ 114 | 115 | /* Begin PBXProject section */ 116 | 1623BA10275A05B100CCB3AE /* Project object */ = { 117 | isa = PBXProject; 118 | attributes = { 119 | BuildIndependentTargetsInParallel = 1; 120 | LastSwiftUpdateCheck = 1310; 121 | LastUpgradeCheck = 1310; 122 | TargetAttributes = { 123 | 1623BA17275A05B100CCB3AE = { 124 | CreatedOnToolsVersion = 13.1; 125 | }; 126 | }; 127 | }; 128 | buildConfigurationList = 1623BA13275A05B100CCB3AE /* Build configuration list for PBXProject "LoadingViewExample" */; 129 | compatibilityVersion = "Xcode 13.0"; 130 | developmentRegion = en; 131 | hasScannedForEncodings = 0; 132 | knownRegions = ( 133 | en, 134 | Base, 135 | ); 136 | mainGroup = 1623BA0F275A05B100CCB3AE; 137 | productRefGroup = 1623BA19275A05B100CCB3AE /* Products */; 138 | projectDirPath = ""; 139 | projectRoot = ""; 140 | targets = ( 141 | 1623BA17275A05B100CCB3AE /* LoadingViewExample */, 142 | ); 143 | }; 144 | /* End PBXProject section */ 145 | 146 | /* Begin PBXResourcesBuildPhase section */ 147 | 1623BA16275A05B100CCB3AE /* Resources */ = { 148 | isa = PBXResourcesBuildPhase; 149 | buildActionMask = 2147483647; 150 | files = ( 151 | 1623BA23275A05B300CCB3AE /* Preview Assets.xcassets in Resources */, 152 | 1623BA20275A05B300CCB3AE /* Assets.xcassets in Resources */, 153 | ); 154 | runOnlyForDeploymentPostprocessing = 0; 155 | }; 156 | /* End PBXResourcesBuildPhase section */ 157 | 158 | /* Begin PBXSourcesBuildPhase section */ 159 | 1623BA14275A05B100CCB3AE /* Sources */ = { 160 | isa = PBXSourcesBuildPhase; 161 | buildActionMask = 2147483647; 162 | files = ( 163 | 1623BA1E275A05B100CCB3AE /* ContentView.swift in Sources */, 164 | 1623BA1C275A05B100CCB3AE /* LoadingViewExampleApp.swift in Sources */, 165 | ); 166 | runOnlyForDeploymentPostprocessing = 0; 167 | }; 168 | /* End PBXSourcesBuildPhase section */ 169 | 170 | /* Begin XCBuildConfiguration section */ 171 | 1623BA24275A05B300CCB3AE /* Debug */ = { 172 | isa = XCBuildConfiguration; 173 | buildSettings = { 174 | ALWAYS_SEARCH_USER_PATHS = NO; 175 | CLANG_ANALYZER_NONNULL = YES; 176 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 177 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 178 | CLANG_CXX_LIBRARY = "libc++"; 179 | CLANG_ENABLE_MODULES = YES; 180 | CLANG_ENABLE_OBJC_ARC = YES; 181 | CLANG_ENABLE_OBJC_WEAK = YES; 182 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 183 | CLANG_WARN_BOOL_CONVERSION = YES; 184 | CLANG_WARN_COMMA = YES; 185 | CLANG_WARN_CONSTANT_CONVERSION = YES; 186 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 187 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 188 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 189 | CLANG_WARN_EMPTY_BODY = YES; 190 | CLANG_WARN_ENUM_CONVERSION = YES; 191 | CLANG_WARN_INFINITE_RECURSION = YES; 192 | CLANG_WARN_INT_CONVERSION = YES; 193 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 194 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 195 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 196 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 197 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 198 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 199 | CLANG_WARN_STRICT_PROTOTYPES = YES; 200 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 201 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 202 | CLANG_WARN_UNREACHABLE_CODE = YES; 203 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 204 | COPY_PHASE_STRIP = NO; 205 | DEBUG_INFORMATION_FORMAT = dwarf; 206 | ENABLE_STRICT_OBJC_MSGSEND = YES; 207 | ENABLE_TESTABILITY = YES; 208 | GCC_C_LANGUAGE_STANDARD = gnu11; 209 | GCC_DYNAMIC_NO_PIC = NO; 210 | GCC_NO_COMMON_BLOCKS = YES; 211 | GCC_OPTIMIZATION_LEVEL = 0; 212 | GCC_PREPROCESSOR_DEFINITIONS = ( 213 | "DEBUG=1", 214 | "$(inherited)", 215 | ); 216 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 217 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 218 | GCC_WARN_UNDECLARED_SELECTOR = YES; 219 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 220 | GCC_WARN_UNUSED_FUNCTION = YES; 221 | GCC_WARN_UNUSED_VARIABLE = YES; 222 | IPHONEOS_DEPLOYMENT_TARGET = 15.0; 223 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 224 | MTL_FAST_MATH = YES; 225 | ONLY_ACTIVE_ARCH = YES; 226 | SDKROOT = iphoneos; 227 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 228 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 229 | }; 230 | name = Debug; 231 | }; 232 | 1623BA25275A05B300CCB3AE /* Release */ = { 233 | isa = XCBuildConfiguration; 234 | buildSettings = { 235 | ALWAYS_SEARCH_USER_PATHS = NO; 236 | CLANG_ANALYZER_NONNULL = YES; 237 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 238 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 239 | CLANG_CXX_LIBRARY = "libc++"; 240 | CLANG_ENABLE_MODULES = YES; 241 | CLANG_ENABLE_OBJC_ARC = YES; 242 | CLANG_ENABLE_OBJC_WEAK = YES; 243 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 244 | CLANG_WARN_BOOL_CONVERSION = YES; 245 | CLANG_WARN_COMMA = YES; 246 | CLANG_WARN_CONSTANT_CONVERSION = YES; 247 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 248 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 249 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 250 | CLANG_WARN_EMPTY_BODY = YES; 251 | CLANG_WARN_ENUM_CONVERSION = YES; 252 | CLANG_WARN_INFINITE_RECURSION = YES; 253 | CLANG_WARN_INT_CONVERSION = YES; 254 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 255 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 256 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 257 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 258 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 259 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 260 | CLANG_WARN_STRICT_PROTOTYPES = YES; 261 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 262 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 263 | CLANG_WARN_UNREACHABLE_CODE = YES; 264 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 265 | COPY_PHASE_STRIP = NO; 266 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 267 | ENABLE_NS_ASSERTIONS = NO; 268 | ENABLE_STRICT_OBJC_MSGSEND = YES; 269 | GCC_C_LANGUAGE_STANDARD = gnu11; 270 | GCC_NO_COMMON_BLOCKS = YES; 271 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 272 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 273 | GCC_WARN_UNDECLARED_SELECTOR = YES; 274 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 275 | GCC_WARN_UNUSED_FUNCTION = YES; 276 | GCC_WARN_UNUSED_VARIABLE = YES; 277 | IPHONEOS_DEPLOYMENT_TARGET = 15.0; 278 | MTL_ENABLE_DEBUG_INFO = NO; 279 | MTL_FAST_MATH = YES; 280 | SDKROOT = iphoneos; 281 | SWIFT_COMPILATION_MODE = wholemodule; 282 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 283 | VALIDATE_PRODUCT = YES; 284 | }; 285 | name = Release; 286 | }; 287 | 1623BA27275A05B300CCB3AE /* Debug */ = { 288 | isa = XCBuildConfiguration; 289 | buildSettings = { 290 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 291 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 292 | CODE_SIGN_STYLE = Automatic; 293 | CURRENT_PROJECT_VERSION = 1; 294 | DEVELOPMENT_ASSET_PATHS = "\"LoadingViewExample/Preview Content\""; 295 | ENABLE_PREVIEWS = YES; 296 | GENERATE_INFOPLIST_FILE = YES; 297 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 298 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 299 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 300 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 301 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 302 | LD_RUNPATH_SEARCH_PATHS = ( 303 | "$(inherited)", 304 | "@executable_path/Frameworks", 305 | ); 306 | MARKETING_VERSION = 1.0; 307 | PRODUCT_BUNDLE_IDENTIFIER = sk.jurajmacak.LoadingViewExample; 308 | PRODUCT_NAME = "$(TARGET_NAME)"; 309 | SWIFT_EMIT_LOC_STRINGS = YES; 310 | SWIFT_VERSION = 5.0; 311 | TARGETED_DEVICE_FAMILY = "1,2"; 312 | }; 313 | name = Debug; 314 | }; 315 | 1623BA28275A05B300CCB3AE /* Release */ = { 316 | isa = XCBuildConfiguration; 317 | buildSettings = { 318 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 319 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 320 | CODE_SIGN_STYLE = Automatic; 321 | CURRENT_PROJECT_VERSION = 1; 322 | DEVELOPMENT_ASSET_PATHS = "\"LoadingViewExample/Preview Content\""; 323 | ENABLE_PREVIEWS = YES; 324 | GENERATE_INFOPLIST_FILE = YES; 325 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 326 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 327 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 328 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 329 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 330 | LD_RUNPATH_SEARCH_PATHS = ( 331 | "$(inherited)", 332 | "@executable_path/Frameworks", 333 | ); 334 | MARKETING_VERSION = 1.0; 335 | PRODUCT_BUNDLE_IDENTIFIER = sk.jurajmacak.LoadingViewExample; 336 | PRODUCT_NAME = "$(TARGET_NAME)"; 337 | SWIFT_EMIT_LOC_STRINGS = YES; 338 | SWIFT_VERSION = 5.0; 339 | TARGETED_DEVICE_FAMILY = "1,2"; 340 | }; 341 | name = Release; 342 | }; 343 | /* End XCBuildConfiguration section */ 344 | 345 | /* Begin XCConfigurationList section */ 346 | 1623BA13275A05B100CCB3AE /* Build configuration list for PBXProject "LoadingViewExample" */ = { 347 | isa = XCConfigurationList; 348 | buildConfigurations = ( 349 | 1623BA24275A05B300CCB3AE /* Debug */, 350 | 1623BA25275A05B300CCB3AE /* Release */, 351 | ); 352 | defaultConfigurationIsVisible = 0; 353 | defaultConfigurationName = Release; 354 | }; 355 | 1623BA26275A05B300CCB3AE /* Build configuration list for PBXNativeTarget "LoadingViewExample" */ = { 356 | isa = XCConfigurationList; 357 | buildConfigurations = ( 358 | 1623BA27275A05B300CCB3AE /* Debug */, 359 | 1623BA28275A05B300CCB3AE /* Release */, 360 | ); 361 | defaultConfigurationIsVisible = 0; 362 | defaultConfigurationName = Release; 363 | }; 364 | /* End XCConfigurationList section */ 365 | 366 | /* Begin XCSwiftPackageProductDependency section */ 367 | 16E766BD275A07A000E879D7 /* LoadingButton */ = { 368 | isa = XCSwiftPackageProductDependency; 369 | productName = LoadingButton; 370 | }; 371 | /* End XCSwiftPackageProductDependency section */ 372 | }; 373 | rootObject = 1623BA10275A05B100CCB3AE /* Project object */; 374 | } 375 | -------------------------------------------------------------------------------- /LoadingViewExample/LoadingViewExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LoadingViewExample/LoadingViewExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LoadingViewExample/LoadingViewExample/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /LoadingViewExample/LoadingViewExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /LoadingViewExample/LoadingViewExample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /LoadingViewExample/LoadingViewExample/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // LoadingViewExample 4 | // 5 | // Created by Juraj Macák on 03/12/2021. 6 | // 7 | 8 | import SwiftUI 9 | import LoadingButton 10 | 11 | struct ContentView: View { 12 | 13 | @State private var name: String = "" 14 | 15 | @State private var isLoading: Bool = false 16 | @FocusState private var isFocus: Bool 17 | 18 | private var style: LoadingButtonStyle { 19 | return .init(width: 200, height: 40, cornerRadius: 20, backgroundColor: .blue, loadingColor: .blue, strokeWidth: 1, strokeColor: .white) 20 | } 21 | 22 | var body: some View { 23 | VStack { 24 | TextField("Enter your name", text: $name) 25 | .textContentType(.name) 26 | .focused($isFocus) 27 | 28 | LoadingButton(action: { 29 | isFocus = false 30 | 31 | DispatchQueue.main.asyncAfter(deadline: .now() + 2) {self.isLoading = false 32 | } 33 | }, isLoading: $isLoading, style: style) { 34 | Text("Log in") 35 | } 36 | .foregroundColor(.white) 37 | } 38 | .padding() 39 | } 40 | } 41 | 42 | struct ContentView_Previews: PreviewProvider { 43 | static var previews: some View { 44 | ContentView() 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /LoadingViewExample/LoadingViewExample/LoadingViewExampleApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoadingViewExampleApp.swift 3 | // LoadingViewExample 4 | // 5 | // Created by Juraj Macák on 03/12/2021. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct LoadingViewExampleApp: App { 12 | var body: some Scene { 13 | WindowGroup { 14 | ContentView() 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /LoadingViewExample/LoadingViewExample/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 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: "LoadingButton", 8 | platforms: [ 9 | .iOS(.v13), 10 | .macOS(.v11) 11 | ], 12 | products: [ 13 | .library( 14 | name: "LoadingButton", 15 | targets: ["LoadingButton"]), 16 | ], 17 | targets: [ 18 | .target( 19 | name: "LoadingButton", 20 | dependencies: []) 21 | ] 22 | ) 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Project logo 3 |

4 | 5 |

🔄 SwiftUI LoadingButton 🔄

6 | 7 |
8 | 9 | ![License](https://img.shields.io/github/license/Changemin/LoadingButton?style=for-the-badge) 10 | ![Release](https://img.shields.io/github/v/release/Changemin/LoadingButton?style=for-the-badge) 11 | 12 | 13 |
14 | 15 | --- 16 | 17 |

18 | Simple Loading Animation Button for SwiftUI 19 |
20 |

21 | 22 | ## 📹 Preview 23 | 24 |

25 | 26 |

27 | 28 | ## 🏁 Getting Started 29 | 30 | ### Requirements 31 | * Xcode 11+ 32 | * SwiftUI 33 | * iOS 14+ 34 | * macOS 10.15+ 35 | 36 | ### Installaion 37 | #### Swift Package Manager(SPM) 38 | File ➜ Swift Packages ➜ Add Package Dependancy.. 39 | 40 | ```Swift 41 | .package(url: "https://github.com/Changemin/LoadingButton", from: "1.1.2") 42 | ``` 43 | 44 | ## 🎈Usage 45 | ```Swift 46 | LoadingButton(action: { }, isLoading: Bool, style: LoadingButtonStyle) { 47 | // View on the button 48 | // style is optional parameter 49 | ``` 50 | * `action` : Actions to do when button clicked 51 | * `isLoading` : `Bool` type. you can control loading status with this. 52 | * `style`(Optional) : Custom style with `LoadingButtonStyle` 53 | 54 | #### 🛠Custom Modifiers 55 | ```Swift 56 | LoadingButtonStyle(width: CGFloat, 57 | height: CGFloat, 58 | cornerRadius: CGFloat, 59 | backgroundColor: Color, 60 | loadingColor: Color, 61 | strokeWidth: CGFloat, 62 | strokeColor: Color) 63 | 64 | // All of the parameter is optional 65 | ``` 66 | * `width`(Optional) : Width of button 67 | * `height`(Optional) : Height of button 68 | * `cornerRadius`(Optional) : Corner radius of button 69 | * `backgroundColor`(Optional) : Background color of button 70 | * `loadingColor`(Optional) : Background color of button when `Loading`, default is 50% opacity of `backgroundColor` 71 | * `strokeWidth`(Optional) : Circle loading indicator stroke width 72 | * `strokeColor`(Optional) : Circle loading indicator stroke Color(default: gray) 73 | 74 | ## Example 75 | #### 👶 Simple 76 | ```Swift 77 | import SwiftUI 78 | import LoadingButton 79 | 80 | struct ContentView: View { 81 | @State var isLoading: Bool = false 82 | 83 | var body: some View { 84 | LoadingButton(action: { 85 | // Your Action here 86 | }, isLoading: $isLoading) { 87 | Text("LoadingButton").foregroundColor(Color.white) 88 | } 89 | } 90 | } 91 | ``` 92 | ### Result 93 | 94 | 95 | 96 | ### 💅🏻 Applying Fully Custom Style 97 | ```Swift 98 | import SwiftUI 99 | import LoadingButton 100 | 101 | struct ContentView: View { 102 | @State var isLoading: Bool = false 103 | var style = LoadingButtonStyle(width: 312, 104 | height: 54, 105 | cornerRadius: 27, 106 | backgroundColor: .orange, 107 | loadingColor: Color.orange.opacity(0.5), 108 | strokeWidth: 5, 109 | strokeColor: .gray) 110 | 111 | var body: some View { 112 | LoadingButton(action: { 113 | // Your Action here 114 | }, isLoading: $isLoading, style: style) { 115 | Text("Styled LoadingButton").foregroundColor(Color.white) 116 | } 117 | } 118 | } 119 | ``` 120 | ### Result 121 | 122 | 123 | 124 | ### 💅🏻 Styling Only Needs 125 | ```Swift 126 | import SwiftUI 127 | import LoadingButton 128 | 129 | struct ContentView: View { 130 | @State var isLoading: Bool = false 131 | 132 | var body: some View { 133 | LoadingButton(action: { 134 | // Your Action here 135 | }, isLoading: $isLoading, style: LoadingButtonStyle(cornerRadius: 27, backgroundColor: .orange)) { 136 | Text("Styled LoadingButton").foregroundColor(Color.white) 137 | } 138 | } 139 | } 140 | ``` 141 | ### Same Result 142 | 143 | 144 | 145 | 146 | ## ✅ TODO 147 | - [ ] End animation(normal, shake(when fail), expand) 148 | - [ ] Support gradient background color 149 | 150 | ## 📜 License 151 | 152 | LoadingButton is available under the MIT license. See the `LICENSE` file for more info. 153 | 154 | ## ✍️ Author 155 | 156 | - Byun Kyung Min ➜ [🇰🇷@Changemin](https://github.com/Changemin) 157 | -------------------------------------------------------------------------------- /Sources/LoadingButton/CircleLoadingBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftUIView.swift 3 | // 4 | // 5 | // Created by 변경민 on 2020/12/04. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CircleLoadingBar: View { 11 | @State private var isLoading = false 12 | @State var style: LoadingButtonStyle 13 | 14 | var body: some View { 15 | Circle() 16 | .trim(from: 0, to: 0.7) 17 | .stroke(style.strokeColor, style: StrokeStyle(lineWidth: style.strokeWidth, lineCap: .round, lineJoin: .round)) 18 | .frame(width: style.height - 20, height: style.height - 20) 19 | .rotationEffect(Angle(degrees: isLoading ? 360 : 0)) 20 | .animation(Animation.default.repeatForever(autoreverses: false), value: isLoading) 21 | .onAppear() { 22 | self.isLoading = true 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Sources/LoadingButton/LoadingButton.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | public struct LoadingButton: View{ 4 | @Binding var isLoading: Bool 5 | 6 | var style: LoadingButtonStyle 7 | let content: Content 8 | var action: () -> () = {} 9 | 10 | public init(action: @escaping () -> Void, isLoading: Binding, style: LoadingButtonStyle? = nil, @ViewBuilder builder: () -> Content) { 11 | self._isLoading = isLoading 12 | self.style = style ?? LoadingButtonStyle() 13 | content = builder() 14 | self.action = action 15 | } 16 | 17 | public var body: some View { 18 | Button(action: { 19 | if !isLoading { 20 | action() 21 | } 22 | isLoading = true 23 | }) { 24 | ZStack { 25 | Rectangle() 26 | .fill(isLoading ? style.loadingBackgroundColor : style.backgroundColor) 27 | .frame(width: isLoading ? style.height : style.width, height: style.height) 28 | .cornerRadius(isLoading ? style.height/2 : style.cornerRadius) 29 | 30 | if isLoading { 31 | CircleLoadingBar(style: style) 32 | } 33 | else { 34 | VStack { content } 35 | } 36 | } 37 | } 38 | .frame(width: style.width, height: style.height) 39 | .disabled(isLoading) 40 | .animation(.easeInOut, value: isLoading) 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Sources/LoadingButton/LoadingButtonStyle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftUIView.swift 3 | // 4 | // 5 | // Created by 변경민 on 2020/12/02. 6 | // 7 | 8 | import SwiftUI 9 | 10 | /// A Fully Configuarable Button Style 11 | public struct LoadingButtonStyle { 12 | public init(width: CGFloat? = nil, 13 | height: CGFloat? = nil, 14 | cornerRadius: CGFloat? = nil, 15 | backgroundColor: Color? = nil, 16 | loadingColor: Color? = nil, 17 | strokeWidth: CGFloat? = nil, 18 | strokeColor: Color? = nil) { 19 | self.width = width ?? 312 20 | self.height = height ?? 54 21 | self.cornerRadius = cornerRadius ?? 0 22 | self.backgroundColor = backgroundColor ?? Color.blue 23 | self.loadingBackgroundColor = loadingColor ?? self.backgroundColor.opacity(0.6) 24 | self.strokeWidth = strokeWidth ?? 5 25 | self.strokeColor = strokeColor ?? Color.gray.opacity(0.6) 26 | } 27 | 28 | /// Width of button 29 | public var width: CGFloat = 312 30 | /// Height of button 31 | public var height: CGFloat = 54 32 | /// Corner radius of button 33 | public var cornerRadius: CGFloat = 0 34 | /// Background color of button 35 | public var backgroundColor: Color = .blue 36 | /// Background color of button when loading. 50% opacity of background color gonna be set if blank. 37 | public var loadingBackgroundColor: Color = Color.blue.opacity(0.5) 38 | /// Width of circle loading bar stroke 39 | public var strokeWidth: CGFloat = 5 40 | /// Color of circle loading bar stroke 41 | public var strokeColor: Color = Color.gray.opacity(0.6) 42 | } 43 | -------------------------------------------------------------------------------- /imgs/Example-1-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changemin/LoadingButton/099aed136cb3c5a08c7b98bd9847edf44a76b4cf/imgs/Example-1-1.png -------------------------------------------------------------------------------- /imgs/Example-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changemin/LoadingButton/099aed136cb3c5a08c7b98bd9847edf44a76b4cf/imgs/Example-1.png -------------------------------------------------------------------------------- /imgs/Example-2-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changemin/LoadingButton/099aed136cb3c5a08c7b98bd9847edf44a76b4cf/imgs/Example-2-1.png -------------------------------------------------------------------------------- /imgs/Example-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changemin/LoadingButton/099aed136cb3c5a08c7b98bd9847edf44a76b4cf/imgs/Example-2.png -------------------------------------------------------------------------------- /imgs/Logo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changemin/LoadingButton/099aed136cb3c5a08c7b98bd9847edf44a76b4cf/imgs/Logo.gif -------------------------------------------------------------------------------- /imgs/Preview-zoom.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changemin/LoadingButton/099aed136cb3c5a08c7b98bd9847edf44a76b4cf/imgs/Preview-zoom.gif -------------------------------------------------------------------------------- /imgs/Preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changemin/LoadingButton/099aed136cb3c5a08c7b98bd9847edf44a76b4cf/imgs/Preview.gif -------------------------------------------------------------------------------- /imgs/Screenshot-Loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changemin/LoadingButton/099aed136cb3c5a08c7b98bd9847edf44a76b4cf/imgs/Screenshot-Loading.png -------------------------------------------------------------------------------- /imgs/Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changemin/LoadingButton/099aed136cb3c5a08c7b98bd9847edf44a76b4cf/imgs/Screenshot.png --------------------------------------------------------------------------------