";
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 |
3 |
4 |
5 | 🔄 SwiftUI LoadingButton 🔄
6 |
7 |
8 |
9 | 
10 | 
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
--------------------------------------------------------------------------------